我用JavaFX编写了一个Toe游戏的程序,我正在寻找它的代码回顾,以提高我在Java中的技能和实践。如果你们审查员特别强调以下几点,我们将不胜感激:
package tictactoe;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TicTacToe extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("TicTacToe.fxml"));
stage.setTitle("TicTacToe by Hassan Althaf");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}package tictactoe;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.Random;
import javafx.scene.input.MouseEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.shape.Circle;
import javafx.scene.control.Label;
import javafx.event.ActionEvent;
public class TicTacToeController implements Initializable {
@FXML
private Circle CircleOne;
@FXML
private Circle CircleTwo;
@FXML
private Circle CircleThree;
@FXML
private Circle CircleFour;
@FXML
private Circle CircleFive;
@FXML
private Circle CircleSix;
@FXML
private Circle CircleSeven;
@FXML
private Circle CircleEight;
@FXML
private Circle CircleNine;
@FXML
private Label XOne;
@FXML
private Label XTwo;
@FXML
private Label XThree;
@FXML
private Label XFour;
@FXML
private Label XFive;
@FXML
private Label XSix;
@FXML
private Label XSeven;
@FXML
private Label XEight;
@FXML
private Label XNine;
@FXML
private Label lblMessages;
private int[] filledSquares = new int[9];
private int[] filledCircles = new int[5];
private int[] filledX = new int[5];
private int filledSquaresCounter = 0;
private int filledCirclesCounter = 0;
private int filledXCounter = 0;
private char winningTeam;
final private int[][] winningPositions = {
{1, 5, 9},
{3, 5, 7},
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{1, 4, 7},
{2, 5, 8},
{3, 6, 9}
};
private boolean allowMoves = true;
private boolean tie = false;
@FXML
public void handleSquareOneClick(MouseEvent event) {
this.handleSquareClick(1);
}
@FXML
public void handleSquareTwoClick(MouseEvent event) {
this.handleSquareClick(2);
}
@FXML
public void handleSquareThreeClick(MouseEvent event) {
this.handleSquareClick(3);
}
@FXML
public void handleSquareFourClick(MouseEvent event) {
this.handleSquareClick(4);
}
@FXML
public void handleSquareFiveClick(MouseEvent event) {
this.handleSquareClick(5);
}
@FXML
public void handleSquareSixClick(MouseEvent event) {
this.handleSquareClick(6);
}
@FXML
public void handleSquareSevenClick(MouseEvent event) {
this.handleSquareClick(7);
}
@FXML
public void handleSquareEightClick(MouseEvent event) {
this.handleSquareClick(8);
}
@FXML
public void handleSquareNineClick(MouseEvent event) {
this.handleSquareClick(9);
}
public void handleSquareClick(int squareNumber) {
if(!isAlreadySelectedBox(squareNumber) && this.allowMoves == true) {
switch(squareNumber) {
case 1:
this.showCircleOne();
break;
case 2:
this.showCircleTwo();
break;
case 3:
this.showCircleThree();
break;
case 4:
this.showCircleFour();
break;
case 5:
this.showCircleFive();
break;
case 6:
this.showCircleSix();
break;
case 7:
this.showCircleSeven();
break;
case 8:
this.showCircleEight();
break;
case 9:
this.showCircleNine();
break;
default:
System.out.println("Impossible choice");
break;
}
this.filledSquares[this.filledSquaresCounter] = squareNumber;
this.filledCircles[this.filledCirclesCounter] = squareNumber;
this.filledSquaresCounter++;
this.filledCirclesCounter++;
if(this.checkVictory()) {
this.endGame();
} else {
this.playRandomMove();
if(this.checkVictory()) {
this.endGame();
}
}
} else if(this.filledSquaresCounter >= 9) {
this.tie = true;
this.endGame();
}
}
public boolean isAlreadySelectedBox(int squareNumber) {
boolean found = false;
for(int filledSquare : this.filledSquares) {
if(squareNumber == filledSquare) {
found = true;
}
}
return found == true;
}
public boolean checkVictory() {
if(this.filledCirclesCounter < 3 && this.filledXCounter < 3) {
return false;
}
for(int[] filled : this.winningPositions) {
int slotCounter = 0;
for(int singleFilled : filled) {
if(this.isOccupiedByCircle(singleFilled)) {
slotCounter++;
}
}
if(slotCounter == 3) {
this.winningTeam = 'O';
this.allowMoves = false;
return true;
}
slotCounter = 0;
for(int singleFilled : filled) {
if(this.isOccupiedByX(singleFilled)) {
slotCounter++;
}
}
if(slotCounter == 3) {
this.winningTeam = 'X';
this.allowMoves = false;
return true;
}
}
return false;
}
public void showCircleOne() {
this.CircleOne.setVisible(true);
}
public void showCircleTwo() {
this.CircleTwo.setVisible(true);
}
public void showCircleThree() {
this.CircleThree.setVisible(true);
}
public void showCircleFour() {
this.CircleFour.setVisible(true);
}
public void showCircleFive() {
this.CircleFive.setVisible(true);
}
public void showCircleSix() {
this.CircleSix.setVisible(true);
}
public void showCircleSeven() {
this.CircleSeven.setVisible(true);
}
public void showCircleEight() {
this.CircleEight.setVisible(true);
}
public void showCircleNine() {
this.CircleNine.setVisible(true);
}
public void playRandomMove() {
Random random = new Random();
int result = random.nextInt(9 - 1 + 1) + 1;;
if(this.filledSquaresCounter < 9) {
while(this.isAlreadySelectedBox(result)) {
result = random.nextInt(9 - 1 + 1) + 1;
}
switch(result) {
case 1:
this.showXOne();
break;
case 2:
this.showXTwo();
break;
case 3:
this.showXThree();
break;
case 4:
this.showXFour();
break;
case 5:
this.showXFive();
break;
case 6:
this.showXSix();
break;
case 7:
this.showXSeven();
break;
case 8:
this.showXEight();
break;
case 9:
this.showXNine();
break;
default:
System.out.println("Impossible choice");
break;
}
this.filledSquares[this.filledSquaresCounter] = result;
this.filledX[this.filledXCounter] = result;
this.filledSquaresCounter++;
this.filledXCounter++;
} else {
this.tie = true;
this.endGame();
}
}
public void showXOne() {
this.XOne.setVisible(true);
}
public void showXTwo() {
this.XTwo.setVisible(true);
}
public void showXThree() {
this.XThree.setVisible(true);
}
public void showXFour() {
this.XFour.setVisible(true);
}
public void showXFive() {
this.XFive.setVisible(true);
}
public void showXSix() {
this.XSix.setVisible(true);
}
public void showXSeven() {
this.XSeven.setVisible(true);
}
public void showXEight() {
this.XEight.setVisible(true);
}
public void showXNine() {
this.XNine.setVisible(true);
}
public boolean isOccupiedByCircle(int circlePosition) {
boolean found = false;
for(int filledCircle : this.filledCircles) {
if(filledCircle == circlePosition) {
found = true;
}
}
return found == true;
}
public boolean isOccupiedByX(int xPosition) {
boolean found = false;
for(int filled : this.filledX) {
if(filled == xPosition) {
found = true;
}
}
return found == true;
}
public void endGame() {
this.allowMoves = false;
if(this.tie == true) {
this.lblMessages.setText("It was a tie!");
} else if(String.valueOf(this.winningTeam).equals("O")) {
this.lblMessages.setText("You win!");
} else if(String.valueOf(this.winningTeam).equals("X")) {
this.lblMessages.setText("Sorry, you lose!");
}
}
@FXML
public void handleResetButton(ActionEvent event) {
this.CircleOne.setVisible(false);
this.CircleTwo.setVisible(false);
this.CircleThree.setVisible(false);
this.CircleFour.setVisible(false);
this.CircleFive.setVisible(false);
this.CircleSix.setVisible(false);
this.CircleSeven.setVisible(false);
this.CircleEight.setVisible(false);
this.CircleNine.setVisible(false);
this.XOne.setVisible(false);
this.XTwo.setVisible(false);
this.XThree.setVisible(false);
this.XFour.setVisible(false);
this.XFive.setVisible(false);
this.XSix.setVisible(false);
this.XSeven.setVisible(false);
this.XEight.setVisible(false);
this.XNine.setVisible(false);
this.winningTeam = 0;
this.allowMoves = true;
this.tie = false;
this.lblMessages.setText("");
this.filledSquares = new int[9];
this.filledCircles = new int[5];
this.filledX = new int[5];
this.filledSquaresCounter = 0;
this.filledCirclesCounter = 0;
this.filledXCounter = 0;
}
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="450.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tictactoe.TicTacToeController">
<children>
<Line endX="150.0" layoutX="300.0" layoutY="150.0" startX="-150.0" />
<Line endY="150.0" layoutX="250.0" layoutY="200.0" startY="-150.0" />
<Line endY="150.0" layoutX="350.0" layoutY="200.0" startY="-150.0" />
<Line endX="150.0" layoutX="300.0" layoutY="250.0" startX="-150.0" />
<Rectangle fx:id="SquareOne" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="151.0" layoutY="51.0" onMouseClicked="#handleSquareOneClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareTwo" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="251.0" layoutY="50.0" onMouseClicked="#handleSquareTwoClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareThree" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="351.0" layoutY="51.0" onMouseClicked="#handleSquareThreeClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareFour" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="150.0" layoutY="151.0" onMouseClicked="#handleSquareFourClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareFive" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="251.0" layoutY="151.0" onMouseClicked="#handleSquareFiveClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareSix" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="351.0" layoutY="151.0" onMouseClicked="#handleSquareSixClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareSeven" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="151.0" layoutY="251.0" onMouseClicked="#handleSquareSevenClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareEight" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="251.0" layoutY="251.0" onMouseClicked="#handleSquareEightClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Rectangle fx:id="SquareNine" arcHeight="5.0" arcWidth="5.0" fill="WHITE" height="98.0" layoutX="351.0" layoutY="251.0" onMouseClicked="#handleSquareNineClick" stroke="WHITE" strokeType="INSIDE" width="98.0" />
<Circle fx:id="CircleOne" fill="WHITE" layoutX="200.0" layoutY="99.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleTwo" fill="WHITE" layoutX="300.0" layoutY="99.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleThree" fill="WHITE" layoutX="400.0" layoutY="100.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleFour" fill="WHITE" layoutX="200.0" layoutY="200.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleFive" fill="WHITE" layoutX="300.0" layoutY="200.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleSix" fill="WHITE" layoutX="400.0" layoutY="200.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleSeven" fill="WHITE" layoutX="200.0" layoutY="300.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleEight" fill="WHITE" layoutX="300.0" layoutY="300.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleNine" fill="WHITE" layoutX="400.0" layoutY="300.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Label fx:id="XOne" alignment="CENTER" contentDisplay="CENTER" layoutX="151.0" layoutY="50.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XTwo" alignment="CENTER" contentDisplay="CENTER" layoutX="251.0" layoutY="51.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XThree" alignment="CENTER" contentDisplay="CENTER" layoutX="351.0" layoutY="50.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XFour" alignment="CENTER" contentDisplay="CENTER" layoutX="151.0" layoutY="151.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XFive" alignment="CENTER" contentDisplay="CENTER" layoutX="251.0" layoutY="152.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XSix" alignment="CENTER" contentDisplay="CENTER" layoutX="351.0" layoutY="151.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XSeven" alignment="CENTER" contentDisplay="CENTER" layoutX="151.0" layoutY="251.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XEight" alignment="CENTER" contentDisplay="CENTER" layoutX="251.0" layoutY="251.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="XNine" alignment="CENTER" contentDisplay="CENTER" layoutX="351.0" layoutY="251.0" maxHeight="98.0" maxWidth="98.0" minHeight="98.0" minWidth="98.0" prefHeight="98.0" prefWidth="98.0" text="X" visible="false">
<font>
<Font size="88.0" />
</font>
</Label>
<Label fx:id="lblMessages" alignment="CENTER" contentDisplay="CENTER" layoutX="150.0" layoutY="366.0" prefHeight="20.0" prefWidth="300.0">
<font>
<Font size="16.0" />
</font>
</Label>
<Button fx:id="btnReset" layoutX="274.0" layoutY="398.0" mnemonicParsing="false" onAction="#handleResetButton" text="Reset" />
</children>
</AnchorPane>发布于 2015-07-11 13:34:15
这是一个幸运的选择,实现tac,而不是例如围棋。有了circleOne到circleThreeHundredAndSixtyOne,它可能会变得相当冗长。
你可以看到我最不喜欢的东西。从XML滥用(*)开始,重复性到处传播。
也许FXML允许数组和循环,这可以缩短XML。但是,我们到了另一个点,即XML编程:
只有一件事比那些声称用XML编程的人更糟糕,那就是那些真正用XML编程的人。看蚂蚁的例子。
您的FXML至少有一个小错误,即y坐标99和100。
<Circle fx:id="CircleTwo" fill="WHITE" layoutX="300.0" layoutY="99.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />
<Circle fx:id="CircleThree" fill="WHITE" layoutX="400.0" layoutY="100.0" radius="48.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="8.0" visible="false" />您有一个视图( FXML),一个控制器,但没有模型。模型是存储整个游戏状态并完全独立于GUI的东西。拥有一个模型只具有优势
private Circle CircleOne;根据Java惯例,变量名开始小写。我会选择circle1,因为后缀"One“只会让你看起来更好看。
private int[] filledSquares = new int[9];
private int[] filledCircles = new int[5];
private int[] filledX = new int[5];不知道这是怎么回事。为什么只有5个?也许是因为游戏最多需要9步,也就是5步?您应该使用常量(或注释)来说明这一点。
@FXML
public void handleSquareOneClick(MouseEvent event) {
this.handleSquareClick(1);
}
...
public void handleSquareClick(int squareNumber) {
if(!isAlreadySelectedBox(squareNumber) && this.allowMoves == true) {
switch(squareNumber) {
case 1:
this.showCircleOne();
break;
....
public void showCircleNine() {
this.CircleNine.setVisible(true);
}
...
public void showCircleOne() {
this.CircleOne.setVisible(true);
}
...
public void showXNine() {
this.XOne.setVisible(true);
}因此,由于FXML,每一件事情都重复了9或18次。你需要36个琐碎的方法,一个复杂的开关,你重复,重复,重复.
我是否已经说过,重复的XML会导致重复的代码?
我是否已经说过,重复的XML会导致重复的代码?
我是否已经说过,重复的XML会导致重复的代码?
(*) YMMV,但通常情况下,所有这些“陈述性”实际上是“冗长和重复的”。IMHO、数组、循环和好的布局管理器是要走的路。它也适用于Java。对于抽搐脚趾,GridLayout肯定足够好,请看我的最后一次。
如您所见,我不会使用任何FXML。YMMV,但无论如何,使用数组、循环和一般方法。
private final Circle[] circles = {circle1, circle2, ..., circle9};将所有handleSquareXxxClick替换为
public void handleSquareClick(MouseEvent event) {
handleSquareClick(circles.indexOf(event.getSource());
}(您可能需要将其稍微修改一下,以适应真正的点击源)。
public void handleSquareClick(int squareNumber) {
if(!isAlreadySelectedBox(squareNumber) && this.allowMoves == true) {
circles[squareNumber].setVisible(true);
...我可以看出我错了,有一个类似于模型的东西,即filledSquares和filledCircles,但是对于历史来说,这比找出当前的状态更有用。而不是
public boolean isAlreadySelectedBox(int squareNumber) {
boolean found = false;
for(int filledSquare : this.filledSquares) {
if(squareNumber == filledSquare) {
found = true;
}
}
return found == true;
}顺便说一下。可以简化为
public boolean isAlreadySelectedBox(int squareNumber) {
for(int filledSquare : this.filledSquares) {
if(squareNumber == filledSquare) {
return true;
}
}
return false;
}你本可以
private final boolean[] squareIsFilled = new boolean[9];并将squareIsFilled[squareNumber] = true设置为handleSquareClick。
或者更好,用
enum FieldState {EMPTY, CIRCLE, CROSS}
private final FieldState fieldStates = new FieldState[9];将所有需要的信息保存在一个数组中。
无论您做什么,您都应该致力于减少代码的重复性,因为在当前的状态下,很难看到任何东西。
Model的类,并尽可能地放入其中)class Model {
private int turn;
private final FieldState fieldStates = new FieldState[9];
public boolean canPlay(int index) ...
public void play(int index) ...
public boolean isDecided() ...
public FieldState getWinner() ...
}发布于 2015-07-05 14:11:49
在isOccupiedByX和isOccupiedByCircle方法中返回布尔值的方式过于复杂。
public boolean isOccupiedByX(int xPosition) {
for(int filled : this.filledX) {
if(filled == xPosition) {
return true;
}
}
return false;
}发布于 2015-07-05 14:14:43
各种作业丰富,应简化为可读性。例如:
int result = random.nextInt(9 - 1 + 1) + 1;;有两个分号,其中一个应该删除,9 - 1 + 1计算值为9。应该将其用于参数。
像found == true和this.tie == true这样的条件语句执行不必要的比较。它们可以单独使用:return found;。
https://codereview.stackexchange.com/questions/95870
复制相似问题