如何在删除时设计多对多关系?

时间:2013-03-25 17:01:08

标签: java oop gwt many-to-many java-canvas

我正在使用2个对象创建canvas绘图:RectanglesLines连接矩形。每行应该知道它连接的2 Rectangle。 每个Rectangle都可以有多行将其连接到其他Rectangle

class Rectangle {
    List<Line> connections;
    void setConnection(Line line) {
        connections.add(line);
    }
}

class Line {
    Rectangle from, to;

    public Line(Rectangle from, Rectangle to) {
        this.from = from;
        this.to = to;

        from.setConnection(this);
        to.setConnection(this);
    }
}

我觉得这可能不是一个好设计,因为当我删除Line时,我还必须从它连接的Line中的连接列表中删除Rectangle

当我删除Rectangle时,我还必须删除连接到矩形的Line,因为它们不应该存在。因此,我必须遍历可删除connections的所有Rectangle,并为每个connection获取from / to rectangle,然后再次获取connection列表并删除Line引用。

我的问题不是写代码(我已经有了它),但在我看来,我正在做很多来回的参考。

这可以做得更好吗?不知何故:如果删除了一个矩形,那么来自这些行的所有深层连接都会被自动删除/无效?类似于Hibernate的多对多级联的东西?我不能只使用Hibernate,因为这应该是一个客户端应用程序,没有数据库。

1 个答案:

答案 0 :(得分:1)

基本上你正在构建图表。您需要将边缘与顶点分开。

让我们首先创建一些分离一些问题的接口:

interface Shape {
}

interface ShapeConnection {
    Shape[] getConnectedShapes();
}

然后让我们介绍一个注释,它将标记需要级联删除其连接形状的形状。

@interface CascadeDeleteConnectedShapes {
}

然后可以将Rectangle和Line定义为:

@CascadeDeleteConnectedShapes
class Rectangle implements Shape {

}

class Line implements Shape, ShapeConnection {
    Rectangle from, to;

    public Line(Rectangle from, Rectangle to) {
        this.from = from;
        this.to = to;
    }

    @Override
    public Shape[] getConnectedShapes() {
        return new Shape[] { from, to };
    }
}

最后,你需要一个可以把它放在一起的地方。

class Canvas {
    private ConnectionManager connectionManager = new ConnectionManager();

    private Set<Shape> shapes = new HashSet<Shape>();

    public Canvas() {
    }

    public void removeShape(Shape shape) {
        if (!shapes.remove(shape))
            return; 

        if (shape.getClass().isAnnotationPresent(CascadeDeleteConnectedShapes.class)) {
            cascadeDeleteShape(shape);
        }

        if (shape instanceof ShapeConnection) {
            connectionManager.remove((ShapeConnection) shape);
        }
    }

    private void cascadeDeleteShape(Shape shape) {
        List<ShapeConnection> connections = connectionManager.getConnections(shape);
        for (ShapeConnection connection : connections) {
            if (connection instanceof Shape) {
                this.removeShape((Shape) connection);
            } else {
                connectionManager.remove(connection);
            }
        }
    }

    public void addShape(Shape shape) {
        if (shapes.contains(shape))
            return;

        if (shape instanceof ShapeConnection) {
            addShapeConnection((ShapeConnection) shape);
        }

        shapes.add(shape);
    }

    private void addShapeConnection(ShapeConnection shapeConnection) {
        for (Shape shape : shapeConnection.getConnectedShapes()) {
            if (!shapes.contains(shape))
                throw new Error("cannot connect unknown shapes");
        }
        connectionManager.add(shapeConnection);
    }
}

形状可以同时是形状连接。一旦将几个矩形添加到画布,就可以添加行来连接它们。由于您的设计中的一行被识别为ShapeConnection,因此涉及该行的任何操作都会调用CanvasConnectionManager处理该图表。在此设计中,Line是不可变的非常重要。

通过删除带注释的形状触发级联。您需要仔细管理这些级联:如果在路上某处发生异常,则会留下不完整的图形。

此代码仅用于提供您的想法。此外,我将连接管理器的实现留给您的想象力。其中一条评论中提到了番石榴。一个BiMultiMap会恰到好处地达到你的目的,太糟糕了,他们还没有发布它。无论如何,我当然会考虑让现有的库处理ConnectionManager的细节;已经编写了许多符合您需求的文章。

请注意,此设计中没有circular dependencies