两个对象相互引用可以吗?

时间:2019-02-02 21:48:55

标签: c# class oop

我正在用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,这是一个糟糕的代码气味或者是完全错误的?有什么好的方法可以解决这个问题?

有什么建议吗?预先感谢!

2 个答案:

答案 0 :(得分:3)

我在这里看到的问题是您拥有Piece.fieldField.piece作为公共属性。这意味着其他人可以设置这些属性,而无需更新相应的属性。

另外,当您将一块从一个字段移动到另一个字段时,您不会从前一个字段中删除该块,并且我们允许将块移动到占用的正方形,这将导致多个块引用同一字段,但是该字段仅引用放置在该字段中的最后一块。

要解决这些问题,我将使属性变为只读(使用私有设置器),从而迫使客户端调用相应的SetMove方法来更改它们。然后,在此方法中,我们可以验证要移至的字段是否未被占用(如果存在,我们只是抛出一个异常-客户端必须在调用Move之前首先检查此异常),并清除我们从Piece移出的Field

可以在FieldPiece类中,或在两者中进行验证工作。我将它们全部放在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是否是棋子的有效着陆点(即不允许主教在水平位置)?有什么东西挡住了到达目的地的路径吗?目的地是否被属于同一玩家的另一部分占据?在搬动一件东西之前,有很多事情需要评估。

此外,这将使我们可以在其他类型的游戏中重用FieldField类,这些类可能具有不同的规则集,并具有不同的Piece来实施它们。

答案 1 :(得分:0)

否!这与循环依赖的概念有关。尽管已应用于模块,但很可能将其视为此类模块的前身。

更具体地说,这是相互递归对象的理想示例。从概念上讲,如果您替换(半伪代码)

public class Field
{
    public Piece piece {
        public Field field {
            public Piece piece {
                ...
            }
        }
    }
}

这是因为对象是彼此定义的。那么理论上你可以 做类似的事情

this.field.piece.field.piece...