组合(Composite Pattern)模式:将对象组合成树形结构,以表示“部分-整体”的结构层次,使用户对单个对象和和组合对象的使用具有一致性。

    组合模式常用于树形结构,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点

    image.png

    由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。

    在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。

    优点

    1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
    2. 更容易在组合体内加入新的对象,满足“开闭原则”;


    缺点

    1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
    2. 不容易限制容器中的构件;
    3. 不容易用继承的方法来增加构件的新功能;

    示例:
    在XML或HTML中,从根节点开始,每个节点都可能包含任意个其他节点,这些层层嵌套的节点就构成了一颗树。

    1. public class CompositeDemo {
    2. // 节点接口
    3. interface Node{
    4. // 添加一个节点为子节点
    5. Node add(Node node);
    6. // 获取子节点
    7. List<Node> children();
    8. // 输出为XML
    9. String toXml();
    10. }
    11. // 容器节点,可以包含任意个子节点
    12. static class ElementNode implements Node {
    13. private String name;
    14. private List<Node> list = new ArrayList<>();
    15. public ElementNode(String name) {
    16. this.name = name;
    17. }
    18. public Node add(Node node) {
    19. list.add(node);
    20. return this;
    21. }
    22. public List<Node> children() {
    23. return list;
    24. }
    25. public String toXml() {
    26. String start = "<" + name + ">\n";
    27. String end = "</" + name + ">\n";
    28. StringJoiner sj = new StringJoiner("", start, end);
    29. list.forEach(node -> {
    30. sj.add(node.toXml() + "\n");
    31. });
    32. return sj.toString();
    33. }
    34. }
    35. // 普通节点
    36. static class TextNode implements Node {
    37. private String text;
    38. public TextNode(String text) {
    39. this.text = text;
    40. }
    41. public Node add(Node node) {
    42. throw new UnsupportedOperationException();
    43. }
    44. public List<Node> children() {
    45. return new ArrayList<>();
    46. }
    47. public String toXml() {
    48. return text;
    49. }
    50. }
    51. static class CommentNode implements Node {
    52. private String text;
    53. public CommentNode(String text) {
    54. this.text = text;
    55. }
    56. public Node add(Node node) {
    57. throw new UnsupportedOperationException();
    58. }
    59. public List<Node> children() {
    60. return new ArrayList<>();
    61. }
    62. public String toXml() {
    63. return "<!-- " + text + " -->";
    64. }
    65. }
    66. public static void main(String[] args) {
    67. Node root = new ElementNode("school");
    68. root.add(new ElementNode("classA")
    69. .add(new TextNode("Tom"))
    70. .add(new TextNode("Alice")));
    71. root.add(new ElementNode("classB")
    72. .add(new TextNode("Bob"))
    73. .add(new TextNode("Grace"))
    74. .add(new CommentNode("comment...")));
    75. System.out.println(root.toXml());
    76. }
    77. }

    输入如下

    <school>
    <classA>
    Tom
    Alice
    </classA>
    
    <classB>
    Bob
    Grace
    <!-- comment... -->
    </classB>
    
    </school>