【问题标题】:How to design many-to-many relationships on deletion?如何设计删除时的多对多关系?
【发布时间】:2013-03-15 06:37:43
【问题描述】:

我正在创建一个带有 2 个对象的 canvas 绘图:Rectangles 和 Lines 连接矩形。每条线路都应该知道它连接的 2 个Rectangles。 每个Rectangle 可以有多条线将其连接到其他Rectangles。

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 时,我还必须从它连接的Rectangle 的连接列表中删除Line。

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

我的问题不是编写那个代码(我已经让它工作了),但在我看来,我正在做很多来回引用。

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

【问题讨论】:

  • 为了删除深层连接,需要以一种或另一种方式评估它们。你可以像你一直在做的那样检查删除,或者你可以让它检查访问(将指针设置为空,然后当某些东西尝试访问它并看到空时,它会删除该行)。这样可以节省初始删除的时间,但会为每次访问增加一点开销,并且如果您实际上不访问它们,则会使用更多内存。与垃圾回收非常相似的问题 (en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29)
  • 您可以使用Map 和Rectangle 类型的键和Line 对象的Set 值(或番石榴MultiMap)。如果您有很多矩形和线条,这可以显着提高性能。当然,您会从Rectangle 类中删除List 行。实际上,我想我可能会介绍一个管理“连接点”并包含矩形/线图的类。 (顺便说一句,我认为使用GWT编程时最好使用具体类,例如ArrayList,而不是抽象类List?)
  • 好吧,Map&lt;Rectangle, Set&lt;Line&gt;&gt; 与 List&lt;Rectangle&gt; 有什么不同,Rectangle 拥有 private List&lt;Line&gt; lines?

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


【解决方案1】:

本质上,您是在构建图表。您需要将边与顶点分开。

让我们从创建一些分离一些关注点的接口开始:

interface Shape {
}

interface ShapeConnection {
    Shape[] getConnectedShapes();
}

然后我们引入一个注解,将标记需要级联删除其连接形状的形状。

@interface CascadeDeleteConnectedShapes {
}

矩形和直线可以定义为:

@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,因此涉及该线的任何操作都将调用Canvas 以让ConnectionManager 处理图形。在此设计中,Line 是不可变的,这一点很重要。

移除带注释的形状会触发级联。您需要仔细管理这些级联:如果在途中某处发生异常,您将留下不完整的图表。

此代码仅供您参考。另外,我将连接管理器的实现留给您想象。其中一个 cmets 中提到了番石榴。 BiMultiMap 正好可以满足您的目的,可惜他们还没有发布它。无论如何,我肯定会考虑让现有库处理ConnectionManager 的细节;已经编写了许多可以满足您需求的内容。

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

【讨论】:

  • 使用这些注解是一种非常有趣的方法。谢谢!
猜你喜欢
  • 1970-01-01
  • 2023-01-27
  • 1970-01-01
  • 2021-09-13
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多