模式说明
组合模式并不是我们常说的组合优于继承的那个组合,而是将一组对象组织成树状结构。组合模式往往能够简化复杂的树结构的编码,而且在扩展节点对象时能够更加游刃有余,但如果树结构层次较低,节点较少,相对简单的情况下使用组合模式就是大材小用了。
应用场景
什么场景适合使用组合模式?只要能够构建树状结构的数据就可以使用组合模式。如部门的层级结构,通过部门 ID 和父部门 ID 关联成一课树状结构。还有,XML 格式的数据反序列化为一组对象也符合树状结构,这是因为存在 XML 对象嵌套关系,另外 LDAP 里的用户及组织架构也是树状结构。
组合模式的关系如下图 1 所示,所有的节点都将会继承 Node 对象或实现 Node 接口,而 NodeImpl 中将包含一组 Node 集合,注意这里的引用有可能存在闭环,即互相引用导致死循环,这是组合模式中的其中一个缺点,另外,注意到 Leaf 叶子是可选节点。
图 1
_
如果想要解决组合模式的相互引用导致的遍历时的死循环问题,可以增加 Set<Node> visited 记录已访问的节点,不过 Node 就需要增加 hash() / equal() 方法了,又或者使用 Node.hash() 返回值作为已访问的记录,如 Set<Integer> visited,只需要记录类在内存中的地址 visited.add(Node.hash())。
代码实现
现在,通过组合模式实现 XML 的构建,代码如下。
public interface Node {String name();Map<String, String> attrs();List<Node> children();}
public class ElementNode implements Node {private final String name;private Map<String, String> attrs;private List<Node> children;public ElementNode(String name) {this.name = name;}@Overridepublic String name() {return name;}@Overridepublic Map<String, String> attrs() {return Optional.of(attrs).orElse(Collections.emptyMap());}@Overridepublic List<Node> children() {return Optional.of(children).orElse(Collections.emptyList());}public void setAttrs(String key, String val) {if (attrs == null) attrs = new HashMap<>();if (val == null) { // 表示删除attrs.remove(key);return;}attrs.put(key, val);}public void addChild(Node n) {if (children == null) children = new LinkedList<>();children.add(n);}}
Node html = new ElementNode("html");Node head = new ElementNode("head");head.addChild(new ElementNode("script"));Node body = new ElementNode("body");body.addChild(new ElementNode("div"));html.addChild(head);html.addChild(body);/**大概输出的就是如下的样子<html><head><script></script></head><body><div></div></body></html>*/
