大話設計模式—組合模式

May的部落格發表於2016-04-01

組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的物件當作一個單一的物件。組合模式依據樹形結構來組合物件,用來表示部分以及整體層次。這種型別的設計模式屬於結構型模式,它建立了物件組的樹形結構。這種模式建立了一個包含自己物件組的類。該類提供了修改相同物件組的方式。

大話設計模式中程傑老師給出的定義是,組合模式:將物件組合成樹形結構以表示”部分-整體”的層次結構。組合模式使得使用者對單個物件和組合物件的使用具有一致性

組合模式結構圖:

這裡寫圖片描述

首先,讓我們通過過一個簡單的例項來簡單瞭解一下組合模式做基本的用法。例項演示了一個組織中員工的層次結構。

建立Employee類:

package com.exercise.composite;

import java.util.ArrayList;
import java.util.List;

public class Employee {

    private String name;
    private String dept;
    private int salary;
    private List<Employee> subordinates;//部下

    //constructor
    public Employee(String name, String dept, int salary,
            List<Employee> subordinates) {
        super();
        this.name = name;
        this.dept = dept;
        this.salary = salary;
        this.subordinates = subordinates;
        subordinates = new ArrayList<Employee>();
    }


    public Employee(String name, String dept, int salary) {
        super();
        this.name = name;
        this.dept = dept;
        this.salary = salary;
        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 + " ]";
       } 
}

測試類:

package com.exercise.composite;
/**
 * 使用Employee來建立和列印員工的層次結構
 * @author lmb
 *
 */
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("-------------------公司員工情況----------------------");
      System.out.println(CEO);
      for (Employee headEmployee : CEO.getSubordinates()) {
          //列印CEO的直屬一級部下
         System.out.println(headEmployee);
         for (Employee employee : headEmployee.getSubordinates()) {
             //列印CEO的二級部下
            System.out.println(employee);
         }
      } 

    }

}

執行結果:

這裡寫圖片描述

使用場景:

需求中是體現部分與整體層次結構的時候,以及當我們希望使用者可以忽略組合物件與單個物件的不同,統一的使用組合結構中的所有物件時。簡而言之,就是涉及到部分、整體場景時,如樹形選單,檔案、資料夾的管理。

主要解決:它在我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程式可以向處理簡單元素一樣來處理複雜元素,從而使得客戶程式與複雜元素的內部結構解耦。

如何解決:樹枝和葉子實現統一介面,樹枝內部組合該介面。

一般在工作中說起部分和總體我們想到最多的大概就是總公司和分公司了,如果我們需要做一套辦公管理系統,並且總公司的人力資源部、財務部等的辦公掛曆功能在所有的分公司也都要有,我們該怎麼實現?

功能實現:

這裡寫圖片描述

抽象公司類或介面

package com.composite;

public abstract class Company {

    protected String name;

    public Company(String name){
        this.name = name;
    }

    public abstract void add(Company company);//add
    public abstract void remove(Company company);//remove
    public abstract void display(int depth);//display
    public abstract void lineofDuty();//line of duty

}

具體公司類(樹枝節點)

package com.composite;

import java.util.ArrayList;
import java.util.List;

public class ConcreteCompany extends Company {

    private List<Company> childrenCompany = new ArrayList<Company>();

    public ConcreteCompany(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {
        childrenCompany.add(company);
    }

    @Override
    public void display(int depth) {
        System.out.println("第 " + depth + " 層的機構名為: " + name);
        for (Company c : childrenCompany) {
            c.display(depth + 1);
        }
    }

    @Override
    public void lineofDuty() {
        for (Company c : childrenCompany) {
            c.lineofDuty();
        }
    }

    @Override
    public void remove(Company company) {
        childrenCompany.remove(company);
    }

}

財務部和人力資源部(樹葉節點)

package com.composite;

public class HRDepartment extends Company {

    public HRDepartment(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {

    }

    @Override
    public void display(int depth) {
        System.out.println("第 " + depth + " 層的機構名為: " + name);
    }

    @Override
    public void lineofDuty() {
        System.out.println(name + "    負責員工招聘管理培訓");
    }

    @Override
    public void remove(Company company) {

    }

}
package com.composite;

public class FinanceDepartment extends Company {

    public FinanceDepartment(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {

    }

    @Override
    public void display(int depth) {
        System.out.println("第 " + depth + " 層的機構名為: " + name);
    }

    @Override
    public void lineofDuty() {
        System.out.println(name + "   負責公司財務收支管理");
    }

    @Override
    public void remove(Company company) {

    }

}

測試方法

package com.composite;

public class CompositePatternDemo {

    public static void main(String[] args) {

        //一個總公司
        ConcreteCompany root = new ConcreteCompany("北京總公司");
        root.add(new HRDepartment("總公司人力資源部"));
        root.add(new FinanceDepartment("總公司財務部"));

        //三個子公司
        ConcreteCompany com1 = new ConcreteCompany("廣州分公司");
        com1.add(new HRDepartment("廣州分公司人力資源部"));
        com1.add(new FinanceDepartment("廣州分公司財務部"));
        root.add(com1);

        ConcreteCompany com2 = new ConcreteCompany("杭州分公司");
        com2.add(new HRDepartment("杭州分公司人力資源部"));
        com2.add(new FinanceDepartment("杭州分公司財務部"));
        root.add(com2);

        ConcreteCompany com3 = new ConcreteCompany("深圳分公司");
        com3.add(new HRDepartment("深圳分公司人力資源部"));
        com3.add(new FinanceDepartment("深圳分公司財務部"));
        root.add(com3);

        System.out.println("-------公司結構圖--------");
        root.display(1);
        System.out.println("----------各部門職責----------");
        root.lineofDuty();



    }

}

執行結果:

這裡寫圖片描述

這樣,通過組合模式我們就定義了包含人力資源部和財務部這些基本物件和分公司等組合物件的類層次結構。

基本物件可以被組合成更復雜的組合物件,而這個組合物件又可以被組合,這樣不斷的遞迴下去,在客戶程式碼中,任何用到基本物件的地方都可以使用組合物件了。組合模式讓客戶可以一致的使用組合結構和單個物件。

應用例項:

1、算術表示式包括運算元、操作符和另一個運算元,其中,另一個操作符也可以是操作樹、操作符和另一個運算元。

2、在 JAVA AWT 和 SWING 中,對於 Button 和 Checkbox 是樹葉,Container 是樹枝。

優點:

1、高層模組呼叫簡單。
2、節點自由增加。

缺點:

在使用組合模式時,其葉子和樹枝的宣告都是實現類,而不是介面,違反了依賴倒置原則。

相關文章