我的问题涉及面向对象编程中“组合”的方面。我用java编程,但这适用于任何语言。
我的问题是双向合成。我听说构图意味着“有一种”关系。想象一下,你正在构建一个跳棋游戏,在我的情况下,我将有一个“Board”类用于生成棋盘,棋盘有HAS正方形,而正方形可以有棋子(一个棋子类),或者是空的。组合(根据我的理解)会说我们的棋盘应该有一个方形对象数组,方形对象应该有一个棋子区域。
public class Board {
private List<Square>;
... (generating board, other functions/fields)
}
public class Square {
private Piece piece;
... (other functions/fields)
}
public class Piece {
... (functions/fields)
}
这就是我理解作文的方式。我的问题是,是的,一块板子有正方形和碎片,但是一件作品还有一块板子是否有意义?如果您在片段类中创建了移动方法,则不需要访问该板及其方块吗?像这样的东西:
public class Piece {
private Board board;
... (constructor/functions/fields)
public void move(int x, int y) {
if (this.board.getSquare(x, y).isEmpty())
System.out.println("Piece moves to (x, y)");
... (etc.)
}
}
这被认为是良好的设计实践,还是有更好的方法来实现这一点?
这里有两个问题,真的。双向组合 - 或者,一般来说,双向关联 - 本身不是问题。它是一种绝对有效的关系类型,虽然在实现中有些不切实际。双向关联增加了一个依赖关系,并且每个额外的依赖关系都会增加耦合(请参阅Craig Larman解释的低耦合作为其GRASP原则的一部分)。
尽管如此,有时还需要双向关联来反映两个对象只在一起才有意义的事实。是的,你可以说一块有一块板,但我们真的在现实生活中说过吗?我们关心那个吗?从域名的角度来看,这并不起任何关键作用,至少在域名只是在进行检查时。也许在另一个想象领域,我们在许多董事会之间移动部分,这种观点可能是至关重要的,我们可能需要通过双向关联对其进行建模。现在,提到域名,我不能不提到Eric Evans在域驱动设计上的经典book。实际上,Eric在使用软件解释建模领域的所有细微差别方面做得很好。特别是,他还解释了对象之间的双向(双向)关联(第83页)。
现在,到第二个问题。在您的情况下保持对董事会的引用并因此保持双向关联的必要性是由于分配将部件移动到错误的对象的责任。移动部件是一种可能会影响多个部件的操作,因此一个特定的部件在不破坏封装的情况下无法处理。相反,我们应该问,谁是负责任的对象,可以监督所有的部分,并根据域规则(玩检查规则)以适当的方式管理它们?在没有添加任何特殊对象的情况下,我们已经拥有了这些特殊对象,这似乎是董事会。 Board看起来是一个很好的地方,可以保存操纵电路板内容的逻辑(因此我们尊重封装)。
所以,回答你的实际问题,最好将move()方法放到Board类中,并自动摆脱不必要的双向关联。作为我提到的关于责任分配的后续行动,我再次提到GRASP(它实际上代表一般责任分配原则) - 检查它,我相信它会对这个和类似案例有很大帮助。
circular dependency导致紧密耦合。这通常被认为是不好的做法。紧耦合组件相互依赖。您希望更改Board类的时间,您可能也需要更改Piece类。如果系统增长,这项任务变得越来越困难。
尝试考虑组件的责任。移动自己并控制董事会是否有责任?如果董事会会这样做可能会更好吗?
public class Board {
public void move(Piece piece, int x, int y) {
}
}