本质上,您正在构建图表。您需要将边与顶点分开。
让我们从创建一些分离一些关注点的接口开始:
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
不可变是很重要的。
级联由删除带注释的形状触发。您需要仔细管理这些级联:如果在途中某处发生异常,您将留下不完整的图表。
此代码仅用于为您提供一个想法。另外,我将连接管理器的实现留给您想象。其中一条评论中提到了番石榴。BiMultiMap可以恰到好处地满足您的目的,可惜他们还没有发布它。无论如何,我肯定会考虑让ConnectionManager
现有库处理的细节;已经编写了许多适合您需求的内容。
请注意,此设计中没有循环依赖关系。