组合(整体-部分)模式
简介
组合模式(Composite Pattern)又叫作整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型设计模式。
通用模板
-
创建抽象节点接口
import java.util.List;// 抽象节点 public interface INode {// 添加一个节点为子节点:INode add(INode node);// 获取子节点:List<INode> children();// 操作String operation(); }
-
创建树枝节点
import java.util.ArrayList; import java.util.List;// 树枝节点 public class BranchNode implements INode {private String name;private List<INode> list = new ArrayList<>();public BranchNode(String name) {this.name = name;}@Overridepublic INode add(INode node) {list.add(node);return this;}@Overridepublic List<INode> children() {return list;}@Overridepublic String operation() {StringBuilder builder = new StringBuilder(this.name);for (INode node :list) {builder.append("\n");builder.append(node);}return builder.toString();}@Overridepublic String toString() {return "BranchNode{" +"name='" + name + '\'' +", list=" + list +'}';} }
-
创建叶子节点
import java.util.ArrayList; import java.util.List;// 叶子节点 public class LeafNode implements INode {private String name;public LeafNode(String name) {this.name = name;}@Overridepublic INode add(INode node) {throw new UnsupportedOperationException();}@Overridepublic List<INode> children() {return new ArrayList<>();}@Overridepublic String operation() {return this.name;}@Overridepublic String toString() {return "LeafNode{" +"name='" + name + '\'' +'}';} }
模板测试
-
测试代码
public class Client {public static void main(String[] args) {// 创建一个根节点BranchNode root = new BranchNode("root");// 创建一个树枝节点BranchNode branchA = new BranchNode("branch A");BranchNode branchB = new BranchNode("branch B");// 创建一个叶子节点LeafNode leafA = new LeafNode("leaf A");LeafNode leafB = new LeafNode("leaf B");// 给根节点添加树枝节点Aroot.add(branchA);// 给根节点添加树枝节点Broot.add(branchB);// 给树枝节点A添加叶子节点AbranchA.add(leafA);// 给树枝节点B添加叶子节点BbranchB.add(leafB);String result = root.operation();System.out.println(result);} }
-
结果
root BranchNode{name='branch A', list=[LeafNode{name='leaf A'}]} BranchNode{name='branch B', list=[LeafNode{name='leaf B'}, LeafNode{name='leaf C'}]}
应用场景
在生活中,组合模式非常常见,比如,树形菜单、公司组织架构和操作系统目录结构等。当子系统与其内各个对象层次呈树形结构时,可以使用组合模式让子系统内各个对象层次的行为操作具备一致性。当客户端使用该子系统内任意一个对象时,不用进行区分,直接使用通用操作即可,非常便捷。
总结一下,组合模式主要有以下应用场景。
(1)希望客户端可以忽略组合对象与单个对象的差异。
(2)对象层次具备整体和部分,呈树形结构。
优点
(1)清楚地定义各层次的复杂对象,表示对象的全部或部分层次。
(2)让客户端忽略了层次的差异,方便对整个层次结构进行控制。
(3)简化客户端代码。
(4)符合开闭原则。
缺点
(1)限制类型时会较为复杂。
(2)使设计变得更加抽象。
“生搬硬套”实战
场景描述
我们来看一个具体的例子。在XML或HTML中,从根节点开始,每个节点都可能包含任意个其他节点,这些层层嵌套的节点就构成了一颗树。
代码开发
-
创建抽象节点(这里指的是xml的根节点)接口
import java.util.List; // 节点接口 public interface INode {// 添加一个节点为子节点:INode add(INode INode);// 获取子节点:List<INode> children();// 输出为XML:String toXml(); }
-
创建树枝节点(这里指的是xml的元素标签)
// 元素节点 public class ElementNode implements INode {private String name;private List<INode> list = new ArrayList<>();public ElementNode(String name) {this.name = name;}public INode add(INode INode) {list.add(INode);return this;}public List<INode> children() {return list;}public String toXml() {String start = "<" + name + ">\n";String end = "</" + name + ">\n";StringJoiner sj = new StringJoiner("", start, end);list.forEach(INode -> {sj.add(INode.toXml() + "\n");});return sj.toString();} }
-
创建叶子节点
// 普通文本节点 public class TextNode implements INode{private String text;public TextNode(String text) {this.text = text;}@Overridepublic INode add(INode INode) {throw new UnsupportedOperationException();}@Overridepublic List<INode> children() {return new ArrayList<>();}@Overridepublic String toXml() {return text;} }
// 注释节点 public class CommentNode implements INode {private String text;public CommentNode(String text) {this.text = text;}@Overridepublic INode add(INode INode) {throw new UnsupportedOperationException();}@Overridepublic List<INode> children() {return new ArrayList<>();}@Overridepublic String toXml() {return "<!-- " + text + " -->";} }
至此,我们就通过“生搬硬套”组合模式的模板设计出一套组合xml的代码,接下来我们进行测试:
-
测试代码
public class Client {public static void main(String[] args) {INode root = new ElementNode("school");root.add(new ElementNode("classA").add(new TextNode("Tom")).add(new TextNode("Alice")));root.add(new ElementNode("classB").add(new TextNode("Bob")).add(new TextNode("Grace")).add(new CommentNode("comment...")));System.out.println(root.toXml());} }
-
结果
<school> <classA> Tom Alice </classA><classB> Bob Grace <!-- comment... --> </classB>
总结
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。。这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。