循环依赖的设计

时间:2017-05-15 04:41:02

标签: java circular-dependency

这是一个关于如何更好处理循环依赖的问题。让我先说一下我认为循环依赖很少是必要的,但代码传给了我,我无法帮助它。

假设对具有概念上相同立场的类存在循环依赖,即没有明显的"拥有"他们之间的关系。我该如何优雅地处理它们?

让我们举个例子来说明我们想要表示房间的可变拓扑,neighbour是一个反身属性

interface Room {
    public void remove(Room r);
}

class Living implements Room {
    Room[] neighbour;
    public void remove(Room r) {/* implementation */}
}

class Dining implements Room {
    Room[] neighbour;
    public void remove(Room r) {/* implementation */}
}

现在,显然我们无法在remove的实现中调用另一个Room上的remove,这显然是无休止的递归。但后来我有几个选择:

  • 让一个Room拥有另一个,以某种方式记录事实,然后只有一种Room具有remove能力。
  • 创建第二个方法removeSelf,它从不调用另一个Room的方法,从而解析无限递归。
  • 在层次结构中有一个Building个更高的对象,让它在Room上执行所有操作

它们各自的缺点是

  • 非常反直觉,并且在没有人工结构时施加人工结构。此外,某个邻居中必须至少有一个拥有Room
  • 添加一个永远不应被用户调用的方法,这很糟糕,因为它是一个接口。
  • 需要一个拥有对象,有时候也是一个非常笨拙的结构,Building只是巧合适合成为拥有对象。

所以,问题是,如果循环依赖在某种程度上是不可避免的,那么更好的设计是什么?

我们可能会创建一个RoomOperator类包含在Room上运行的函数,但它也遇到与上面removeSelf方法相同的问题:它最终会调用一个方法在RoomOperator以外非法。

2 个答案:

答案 0 :(得分:0)

解决方案1 ​​

向房间添加功能,允许他们告诉他们是否需要请求邻居删除它们,从而避免递归。

interface Room {
    void remove(Room r);

    void removeOther(Room r);
}

class Living implements Room {
    List<Room> neighbours;

    @Override
    public void remove(Room r) {
        r.removeOther(this);
        neighbours.remove(r);
    }

    @Override
    public void removeOther(Room r) {
        neighbours.remove(r);
    }
}

方法removeOther(Room r)告诉房间只从列表中删除提供的房间。这与remove(Room r)方法形成对比,后者也会导致房间要求提供的房间将其移除。

如果这很重要,这个解决方案可以保持接口方法的完整性。我实际上会让各个房间扩展一个AbstractRoom类,并在那里添加两个方法的实现。但由于您的两个Room都是相同的,因此很难知道该怎么做。

如果您可以将方法更改为remove(Room r, boolean callback),则可以节省一些混淆并仅使用该方法。

为了便于使用,我还将neighbours更改为列表。

解决方案2

使用外部管理器(实用程序)类。该课程允许房间无需相互管理。

interface Room {
    void remove(Room r);

    List<Room> getNeighbours();
}

class Living implements Room {

    List<Room> neighbours;

    @Override
    public void remove(Room r) {
        RoomManager.removeRooms(r, this);
    }

    @Override
    public List<Room> getNeighbours() {
        return neighbours;
    }
}

class RoomManager {

    static void removeRooms(Room r1, Room r2) {
        r1.getNeighbours().remove(r2);
        r2.getNeighbours().remove(r1);
    }
}

同样,我在界面中添加了一个方法,以统一房间的构成。抽象类在这里会做得更好。

答案 1 :(得分:0)

您可以添加管理所有依赖项的基类RoomSupport。其他实现应该扩展RoomSupport。这里的实现:

abstract class RoomSupport implements Room {
    private final Set<Room> neighbours = new HashSet<>();

    @Override
    public void addNeighbour(Room r) {
        if (neighbours.add(r)) {
            r.addNeighbour(this);
        }
    }

    @Override
    public void remove(Room r) {
        if (neighbours.remove(r)) {
            r.remove(this);
        }
    }
}

实现需要注意,添加删除会终止并且根本没有循环依赖。