我正在创建一个棋局引擎。我有一个Piece接口,Rook、Bishop等实现此接口的类。我的棋盘是Piece[][]数组。假设白方玩家想要移动他的Bishop。我将目标坐标和棋盘的引用传递给Bishop。Bishop会检查目标是否在同一条对角线上,然后询问棋盘是否在他的位置和目标方格之间没有任何棋子。从面向对象编程的角度来看这样做是否正确?谢谢
我正在创建一个棋局引擎。我有一个Piece接口,Rook、Bishop等实现此接口的类。我的棋盘是Piece[][]数组。假设白方玩家想要移动他的Bishop。我将目标坐标和棋盘的引用传递给Bishop。Bishop会检查目标是否在同一条对角线上,然后询问棋盘是否在他的位置和目标方格之间没有任何棋子。从面向对象编程的角度来看这样做是否正确?谢谢
这有点微妙。
从面向对象编程的角度来看,人们可以质疑棋盘是否应该首先是一个Piece[][]
数组。在这里,一个好的、干净的、面向对象的设计可能涉及到类似于一个
interface Board {
Piece get(int r, int c);
// Should this be here? See below...
void set(int r, int c, Piece p);
}
然后,一个关键的问题是:主教是否会将“自己”放在给定棋盘的目标位置上?或者,从面向对象编程的角度来看:给定给棋子的棋盘是可变的还是“只读”的?一个人可以想象一个恶意主教
类,当它被赋予Board
时,通过将自己放在国王的位置上暗杀对手的国王。
从非常高层次的抽象面向对象的角度来看,一个Piece
是否应该具有任何智能。一个Piece
是一块愚蠢的塑料。它不知道任何关于国际象棋规则的事情。您(玩家)可以将该棋子放置在任何地方,无论是遵守还是忽略国际象棋规则。因此,棋子绝对不应该遵守或甚至检查任何规则。可以说,遵守规则是从玩家那里期望的,而强制执行规则的遵守是一个高级智能的工作(可能是一些“ChessGameManager”类)。
一个简单的方法,似乎适合于面向对象的国际象棋实现,是拥有像下面这样的类:
abstract class Piece {
Color getColor() { ... }
Point getPosition() { ... }
abstract void doMove(...) { ... }
}
class Bishop extends Piece {
void doMove(....) { ... }
}
// + other classes extending "Piece"
Engine
、Board
、Piece
和Player
类如何相互作用以及它们的责任。 (经过一段时间的思考,您可能会得出结论,您还需要一个Move
类...)。总的来说,检查移动是否有效比起初看起来要复杂得多。您提到,对于主教的移动,您会检查目标位置是否有效以及是否有其他棋子在中间。 但是,如果移动导致自己的国王处于被将军的状态下,则移动仍然无效。 这只能由“引擎”检查,而几乎无法由棋子本身检查。人们往往会忘记的其他事情(涉及整个游戏状态的信息,因此几乎无法由单个棋子处理)是Castling或En passant移动。
对于一个棋类引擎来说,有几个要求使得采用良好的面向对象方法特别困难。如果你的目的是编写一个高效的棋类引擎,那么你的棋盘很可能会是一个由位运算操作的long
值数组....
(顺便提一句:如果您将您的Board
类设计为一个接口,如上所建议,那么您仍然可以在引擎本身使用高级的、面向对象的视图来保持优化表示。我的经验法则是:一开始将所有东西都建模为一个接口。后来再将其具体化是很容易的)
所以,取决于您是想编写一个适用于两个人的象棋游戏(具有规则检查),还是一个象棋引擎,您可能需要以不同的方式解决游戏设计中的某些部分。
编辑:当你在寻找有关国际象棋(引擎)编程的信息时,你一定会遇到这个问题,但我想指出https://www.chessprogramming.org/Main_Page提供了很多背景信息。再次强调:这不是真正关于OO设计,而更多地涉及国际象棋引擎的细节。
...extends Piece
方法的答案)都提出了有效的观点,你应该仔细考虑这些选项。不让棋子“愚笨”也可以,但要考虑到我提到的情况(如易位等)。 - Marco13从设计角度来看,您有两个(或更多)选项需要考虑:
Board is only a place holder / coordinate system for pieces. With this approach you can save a lot of code by having abstract class (or interface, like you wrote, but there will be many common attributes between pieces, so abstract class looks better for me) for Piece, and every type of piece will extend/implement it. Example:
public abstract class Piece
{
private int row;
private int column; // or other method to store position
private boolean isBlack // or enum for type
// contructor, getters, setters etc...
public abstract boolean canMove(int newX, int newY);
/* some other abstract methods if you need */
}
And later
public class Bishop extends Piece
{
@Override
public boolean canMove(int newX, int newY)
{
if( /*check if new points aare on diagonal */)
return true;
else
return false;
}
}
public class Knight extends Piece
{
@Override
public boolean canMove(int newX, int newY)
{
if( /*check if L shaped with prev pos */)
return true;
else
return false;
}
}
canMove()
中,你还需要检查是否有相同玩家的其他棋子,因此你需要访问其他棋子。 - Kuba