Java中的国际象棋游戏:Square类是否应具有“生成可能的动作”方法?

Chess Game in Java: Should Square Class Have “Generate Possible Moves” Method?

我在写象棋游戏。 我的基本设计是要有一个由正方形对象组成的2d数组(8 x 8)。 正方形具有许多字段:int高度,int宽度,计件(如果为空则为null,否则为某种类型的计件对象。

注意:白嘴鸦,骑士,主教等都扩展了Piece。

现在,考虑到我的OOP设计,我对如何弄清楚某个给定零件的合法移动有所了解。 这就是我的想法:

1)用户点击正方形
2)我们确定什么是正方形(如果为空,则返回错误消息)
3)在该广场上为该作品产生合法移动

我担心编写如下代码:

1
2
3
4
5
if (clickedSquare.piece.instanceOf(Rook)) {
    return Rook.getLegalDestinationSquares(clickedSquare);
} else if (clickedSquare.piece.instanceOf(Bishop)) {
    return Bishop.getLegalDestinationSquares(clickedSquare);
} else if...

这似乎真的很糟糕。 必须有一种方法可以更好地符合OOP,但我仍在学习。

谢谢您的帮助,
马里奥格斯


您无需创建该if语句。只需获取当前字段并调用诸如(getLegalMoves())之类的方法即可。
如果字段为空-返回允许移动的空列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class Piece {

    public abstract List<Field> getFieldsAllowed(Field field);
}
public class Rook extends Piece {

    @Override
    public List<Field> getFieldsAllowed(Field field) {
        // TODO Auto-generated method stub
        return null;
    }    
}
public class Field {

    public Piece getPiece() {
        // get current piece
    }
}

这样的事情。尝试找到自己的解决方案。这是不完美的。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public final class Point {
    public final int x, y;

    public Point (int x, int y){
        this.x = x;
        this.y = y;
    }
}

public abstract class Piece {
    private Point location;

    protected Piece (Point initial){
        this.location = initial;
    }

    public Point getLocation(){ return location; }

    public Point setLocation(Point location){ this.location = location; }

    public abstract List<Point> getLegalMoves ();
}

public final class Rook {
    public Rook (Point initial){
        super(initial);
    }

    public List<Point> getLegalMoves (){
         // you know the current location, and you know you are a Rook,
         // so you have all you need to determine the possible points where
         // this Rook can go to
    }
}

然后在其他代码中,您可以执行以下操作:

1
List<Point> legalMoves = clickedSquare.piece.getLegalMoves();

这显然是对其作用的实际部分的抽象。

如果确实需要将静态方法用于其他目的,则可以在每个类(如Rook)中定义它们。将实例方法委托给静态方法,以避免代码重复。像这样:

1
2
3
4
5
6
7
8
9
10
11
12
public final class Rook {
    // constructor etc.

    public List<Point> getLegalMoves (){
         return Rook.getLegalMoves (getLocation());
    }

    public static List<Point> getLegalMoves(Point start){
         // you know the location (start), and you know this method is for a Rook,
         // so you have all you need to determine the possible end points
    }
}

但是,如果不需要该静态方法,则不要使用甚至不要编写它(或者至少不要在类的API中公开它)。否则,班级的用户将开始滥用它,最终将按照您在开始帖子中提供的代码编写代码-数不清的if-elses。

通过使用此解决方案,您将来可以添加更多具体的子类(PawnKing,...),而无需触及任何现有代码(现在作用于Piece),与之相比,它具有可维护性的优点您提供的方法。


来自OOD的有一些批评家。

  • 即使字段上没有数字,字段也是字段。因此,不存在空文件(LSP)!
  • 一个人物永远不知道他能做什么,这只能由游戏规则来设定。创建一个名为Rules的新类。规则的责任是知道他可以做什么(SRP)。国王(例如)必须知道他是否已经做过Castling才能决定是否可以进行Castling,因此他必须知道先前的举动,这会破坏DIP-啊,在这里,我们错过了另一个课程:ChessNotation
  • instanceof在继承事物时很有用,Rook不能继承字段(LSP,DIP)。

  • 如果Rook,Bishop等是:

    1
    2
    3
    4
    5
    6
    7
    public class Rook extends Piece {
        @Override
        public String getLegalDestinationSquares() {
           // returns all leagal Rook squers
           return null;
        }
    }

    和件是:

    1
    2
    3
    public abstract class Piece {
        public abstract String getLegalDestinationSquares();
    }

    然后,您可以像在您的示例中那样:

    1
    return clickedSquare.piece.getLegalDestinationSquares();

    返回字符串仅是示例,可能您应该返回正方形的集合。