0%

【设计模式】Composite Pattern 组合模式

组合模式理解剖析以及应用

组合模式

组合模式(Composite Pattern),有时又叫作整体-部分(Part-Whole)模式,是用于把一组相似的对象当作一个单一的对象。
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

意图

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

解决问题

它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

何时使用?

  1. 您想表示对象的部分-整体层次结构(树形结构)。
  2. 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

应用环境举例

  1. 算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。
  2. 在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
  3. 部分、整体场景,如树形菜单,文件、文件夹的管理。

优点

  1. 高层模块调用简单。
  2. 节点自由增加。
  3. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
  4. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

缺点

  1. 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
  2. 设计较复杂,客户端需要花更多时间理清类之间的层次关系
  3. 不容易限制容器中的构件
  4. 不容易用继承的方法来增加构件的新功能

类图

  • 抽象构件(Component)角色:
    它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
    • 在透明式的组合模式中抽象构件还声明访问和管理子类的接口;
    • 在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
      (总的抽象类或接口,定义一些通用的方法,比如新增、删除)
  • 树叶构件(Leaf)角色:
    是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
  • 树枝构件(Composite)角色 / 中间构件:
    是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

透明方式

  • 在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。’
  • 但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。

安全方式

  • 在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,
  • 但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。

实现

假如要访问集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其对应的树状图如图所示。

透明组合模式

  • 抽象构件
    1
    2
    3
    4
    5
    6
    interface Component {
    public void add(Component c);
    public void remove(Component c);
    public Component getChild(int i);
    public void operation();
    }
  • 树叶构件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Leaf implements Component {
    //成员
    private String name;
    public Leaf(String name) {
    this.name = name;
    }
    //实现接口方法
    public void add(Component c) {}
    public void remove(Component c) {}
    public Component getChild(int i) {return null;}
    public void operation() {
    System.out.println("树叶" + name + ":被访问!");
    }
    }

  • 树枝构件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import java.util.ArrayList;

    class Composite implements Component {
    private ArrayList<Component> children = new ArrayList<Component>();

    public void add(Component c) {
    children.add(c);
    }

    public void remove(Component c) {
    children.remove(c);
    }

    public Component getChild(int i) {
    return children.get(i);
    }

    public void operation() {
    for (Object obj : children) {
    ((Component) obj).operation();
    }
    }
    }
  • 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class CompositePattern {
    public static void main(String[] args) {
    Component c0 = new Composite();
    Component c1 = new Composite();
    Component leaf1 = new Leaf("1");
    Component leaf2 = new Leaf("2");
    Component leaf3 = new Leaf("3");
    c0.add(c1);
    c0.add(leaf1);
    c1.add(leaf2);
    c1.add(leaf3);
    c0.operation();
    }
    }
    结果:
    1
    2
    3
    树叶1:被访问!
    树叶2:被访问!
    树叶3:被访问!

安全组合模式

安全式的组合模式与透明式组合模式的实现代码类似,抽象构件只保留层次的公共行为。

  • 抽象构件
    1
    2
    3
    4
    interface Component {
    //只保留层次的公共行为。
    public void operation();
    }
  • 树叶构件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Leaf implements Component {
    //成员
    private String name;
    public Leaf(String name) {
    this.name = name;
    }
    public void operation() {
    System.out.println("树叶" + name + ":被访问!");
    }
    }

  • 树枝构件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import java.util.ArrayList;

    class Composite implements Component {
    private ArrayList<Component> children = new ArrayList<Component>();

    public void add(Component c) {
    children.add(c);
    }

    public void remove(Component c) {
    children.remove(c);
    }

    public Component getChild(int i) {
    return children.get(i);
    }

    public void operation() {
    for (Object obj : children) {
    ((Component) obj).operation();
    }
    }
    }
  • 测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class CompositePattern {
    public static void main(String[] args) {
    //将树枝构件类型更改为 Composite 类型,以便获取管理子类操作的方法。
    Composite c0 = new Composite();
    Composite c1 = new Composite();
    //树叶构件还是 Component 类型
    Component leaf1 = new Leaf("1");
    Component leaf2 = new Leaf("2");
    Component leaf3 = new Leaf("3");
    c0.add(c1);
    c0.add(leaf1);
    c1.add(leaf2);
    c1.add(leaf3);
    c0.operation();
    }
    }

实例

我们有一个类 Employee,该类被当作组合模型类。 CompositePatternDemo 类使用 Employee 类来添加部门层次结构,并打印所有员工
  • 创建 Employee 类,该类带有 Employee 对象的列表。
    Employee.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import java.util.ArrayList;
    import java.util.List;

    public class Employee {
    private String name;
    private String dept;
    private int salary;
    private List<Employee> subordinates;

    //构造函数
    public Employee(String name,String dept, int sal) {
    this.name = name;
    this.dept = dept;
    this.salary = sal;
    subordinates = new ArrayList<Employee>();
    }

    public void add(Employee e) {
    subordinates.add(e);
    }

    public void remove(Employee e) {
    subordinates.remove(e);
    }

    public List<Employee> getSubordinates(){
    return subordinates;
    }

    public String toString(){
    return ("Employee :[ Name : "+ name +", dept : "+ dept + ", salary :"+ salary+" ]");
    }
    }
  • 使用 Employee 类来创建和打印员工的层次结构。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class CompositePatternDemo {
    public static void main(String[] args) {

    Employee CEO = new Employee("John","CEO", 30000);

    Employee headSales = new Employee("Robert","Head Sales", 20000);
    Employee headMarketing = new Employee("Michel","Head Marketing", 20000);

    Employee clerk1 = new Employee("Laura","Marketing", 10000);
    Employee clerk2 = new Employee("Bob","Marketing", 10000);

    Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
    Employee salesExecutive2 = new Employee("Rob","Sales", 10000);


    CEO.add(headSales);
    CEO.add(headMarketing);

    headSales.add(salesExecutive1);
    headSales.add(salesExecutive2);

    headMarketing.add(clerk1);
    headMarketing.add(clerk2);

    //打印该组织的所有员工
    System.out.println(CEO);
    for (Employee headEmployee : CEO.getSubordinates()) {
    System.out.println(headEmployee);
    for (Employee employee : headEmployee.getSubordinates()) {
    System.out.println(employee);
    }
    }
    }
    }
    结果:
    1
    2
    3
    4
    5
    6
    7
    Employee :[ Name : John, dept : CEO, salary :30000 ]
    Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
    Employee :[ Name : Richard, dept : Sales, salary :10000 ]
    Employee :[ Name : Rob, dept : Sales, salary :10000 ]
    Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
    Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
    Employee :[ Name : Bob, dept : Marketing, salary :10000 ]

感谢查阅