面向对象编程 - 双向组合实践?

Object Oriented Programming - Two Way Composition Practice?

我的问题与面向对象编程中 "composition" 的方面有关。我在 java 编程,但这适用于任何语言。

我的问题是 2-way 组合。我听说组合意味着 "has-a" 关系。想象一下你正在构建一个西洋跳棋游戏,在我的例子中我会有一个 "Board" class 来生成棋盘,棋盘有方块,方块可以有棋子(棋子 class),或者是空的。组合(根据我的理解)会说我们的棋盘中应该有一个方形对象数组,方形对象中应该有一个棋子字段。

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)

}

我是这样理解作文的。我的问题是,是的,一块木板有方块和棋子,但是一块棋子也有木板是不是有意义?如果您在 class 块中创建了一个移动方法,您是否不需要访问棋盘及其方块?像这样:

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.)
    }
}

这是否被认为是良好的设计实践,或者是否有更好的方法来实现它?

circular dependency 导致紧耦合。它通常被认为是不好的做法。紧耦合组件相互依赖。当您决定更换 Board class 时,您可能不得不同时更换 Piece class。而且如果系统越来越大,这个任务就会越来越难。

尝试从组件的责任的角度来思考。棋子自己走,控制棋盘,是棋子的责任吗?如果董事会愿意这样做可能会更好?

public class Board {

    public void move(Piece piece, int x, int y) {

    }
}

这里确实有两个问题。双向组合——或者,一般来说,双向关联——本身不是问题。这是一种绝对有效的关系类型,尽管在实现中有些不切实际。双向关联增加了一个依赖,每增加一个依赖就会增加耦合(检查 Craig Larman 解释的低耦合作为他的 GRASP 原则的一部分)。

不过,双向关联有时是必要的,以反映两个对象只有放在一起才有意义的事实。是的,你可以说一块有一块板,但我们真的在现实生活中这么说吗?我们关心这个吗?从域的角度来看,这并没有起到任何关键作用,至少当域只是进行检查时。也许在另一个虚构的领域中,我们在许多棋盘之间移动棋子,这种观点可能至关重要,我们可能需要用双向关联对其进行建模。现在,在提到域之后,我不能不提到 Eric Evans 关于域驱动设计的 classical book。确实,Eric 很好地解释了使用软件建模领域的所有细微差别。特别是,他还解释了对象之间的双向(双向)关联(第 83 页)。

现在,进入第二期。在您的案例中保持对董事会的引用并因此保持双向关联的必要性是由于分配了将棋子移动到错误对象的责任。移动片段是一种可能会影响多个片段的操作,因此一个特定的片段无法在不破坏封装的情况下处理它。相反,我们应该问,谁是负责监督所有棋子并根据域规则(下棋规则)妥善管理它们的负责人?在不添加任何特殊对象的情况下,在我们已经拥有的对象中,这似乎是棋盘。板看起来是一个很好的地方来保存操纵板内容的逻辑(因此我们尊重封装)。

因此,回答您的实际问题,最好将 move() 方法放在 Board class 中,并自动摆脱不必要的双向关联。作为我提到的关于分配责任的后续行动,我会再次提到 GRASP(它实际上代表一般责任分配原则)- 检查它,我相信它会对这种情况和类似情况有很大帮助。