Composite pattern
对于处理部分整体层次结构非常有用。它有Component
的界面。 Leaf
和Composite
都为Component
接口提供了实现。
Add(Component c)
课程中Remove(Component c)
,GetChild(int position)
和Leaf
方法的实施应该是什么?
我可以使用方法不执行任何操作,或者抛出异常,例如:OperationNotSuportedByLeafException
。 但是这样做会打破Liskov替代原则。处理这些方法的最佳方法是什么?
编辑:另一种方法是在Composite 中移动这些方法。它将是暴露的最顶层接口,即组件。当需要调用add
,remove
操作时,在Composite中移动方法将需要显式转换,这又违反了良好的设计原则。
答案 0 :(得分:2)
当然取决于您的设计目标。如果您的目标是树/图应该可以在任何时间点修改,那么叶子实际上可以成为父节点。在这种情况下,可以在组件中定义与层次结构相关的方法。
另一种方法(虽然我不知道这是否适用于您的用例)是使用以下两个想法:
通过使结构不可变,我们可以将所有图构造推送到构造函数。有了这个,我们回避你提到的类型转换问题,并使整个事情更容易推理。
通过将结构与功能分离,我们不需要向客户发布结构信息,而是提供我们想要提供的功能。有了这个,我们可以保留Liskov,Demeter和其他OO的东西。
这就是它的样子:
public interface Node {
// Here we offer the "functionality", but we don't
// publish the "structure". This is made-up, I
// don't know your use-case.
void process(Consumer<Payload> payloadConsumer);
}
public class Leaf implements Node {
private Payload payload;
public Lead(Payload payload) {
this.payload = payload;
}
@Override
public void process(Consumer<Payload> payloadConsumer) {
payloadConsumer.accept(payload);
}
}
public class ParentNode implements Node {
private List<Node> children;
public ParentNode(Node... children) {
this.children = asList(children);
}
// Here we implement the processing recursively.
// All children can respond according to their semantics,
// instead of assuming any structure beyond what we know.
@Override
public void process(Consumer<Payload> payloadConsumer) {
children.forEach(child -> child.process(payloadConsumer));
}
}
当然,您可以根据要表示的逻辑类型定义自己的Node
类型。您可以定义多个操作,而不仅仅是我编写的一个process()
方法。然后你可以像这样把所有这些插在一起:
Node graph = new ParentNode(
new ParentNode(new Leaf(p1), new Leaf(p2)),
new SpecialLeaf(a, b, c) // Whatever
);
答案 1 :(得分:1)