我正在用C#制作象棋游戏。我有2个班级,分别是Field和Piece:
public class Field
{
// the piece that is standing on this field
// null if no piece is standing on it
public Piece piece { get; set; }
}
public class Piece
{
// the field this piece is standing on
public Field field { get; set; }
}
一块移动时,此方法被调用(在Piece类中):
public void Move(Field field)
{
this.field = field;
field.piece = this;
}
这似乎并没有很好的编码,因为每次我改变字段属性,我也必须改变部分属性,用于现场。不过,我确实需要两个属性,因为在代码的其他地方,我都需要它们进行检查等(例如,该部分在哪个字段上以及该字段在哪个部分上进行提取。)
我的问题:这是完全OK,这是一个糟糕的代码气味或者是完全错误的?有什么好的方法可以解决这个问题?
有什么建议吗?预先感谢!
答案 0 :(得分:3)
我在这里看到的问题是您拥有Piece.field
和Field.piece
作为公共属性。这意味着其他人可以设置这些属性,而无需更新相应的属性。
另外,当您将一块从一个字段移动到另一个字段时,您不会从前一个字段中删除该块,并且我们允许将块移动到占用的正方形,这将导致多个块引用同一字段,但是该字段仅引用放置在该字段中的最后一块。
要解决这些问题,我将使属性变为只读(使用私有设置器),从而迫使客户端调用相应的Set
或Move
方法来更改它们。然后,在此方法中,我们可以验证要移至的字段是否未被占用(如果存在,我们只是抛出一个异常-客户端必须在调用Move
之前首先检查此异常),并清除我们从Piece
移出的Field
。
可以在Field
或Piece
类中,或在两者中进行验证工作。我将它们全部放在Field
类中以简化操作。
即使如此,这仍然存在问题。您可以直接调用Field.SetPiece(piece)
(而不是Piece.MoveTo(field);
),这将使块具有null
的{{1}}值。因此,这只是轻微的改进,而不是理想的解决方案。参见下面的更好的主意。
Field
对此进行了更多思考之后,我认为一个更好的解决方案将是拥有一个public class Field
{
public Piece Piece { get; private set; }
public bool Occupied => Piece != null;
public void ClearPiece()
{
// Remove this field from the piece
if (Piece?.Field == this) Piece.MoveTo(null);
// Remove the piece from this field
Piece = null;
}
public void SetPiece(Piece piece)
{
if (piece != null)
{
if (Occupied)
{
throw new InvalidOperationException(
$"Field is already occupied by {Piece}.");
}
// Remove piece from the piece's previous field
if (piece.Field?.Piece == piece)
{
piece.Field.ClearPiece();
}
}
Piece = piece;
}
}
public class Piece
{
public Field Field { get; private set; }
public void MoveTo(Field field)
{
field.SetPiece(this);
Field = field;
}
}
类来处理所有的验证和移动,然后我们可以使GameManager
和{{1 }}类“哑”。
这是有道理的,因为在Field
上设置Piece
之前还有很多验证工作要做。可以将这块棋子移到该位置吗(即如果国王在检查中并且没有挡住它,则不允许这样做)。根据棋子的移动规则,Piece
是否是棋子的有效着陆点(即不允许主教在水平位置)?有什么东西挡住了到达目的地的路径吗?目的地是否被属于同一玩家的另一部分占据?在搬动一件东西之前,有很多事情需要评估。
此外,这将使我们可以在其他类型的游戏中重用Field
和Field
类,这些类可能具有不同的规则集,并具有不同的Piece
来实施它们。
答案 1 :(得分:0)
否!这与循环依赖的概念有关。尽管已应用于模块,但很可能将其视为此类模块的前身。
更具体地说,这是相互递归对象的理想示例。从概念上讲,如果您替换(半伪代码)
public class Field
{
public Piece piece {
public Field field {
public Piece piece {
...
}
}
}
}
这是因为对象是彼此定义的。那么理论上你可以 做类似的事情
this.field.piece.field.piece...