天天看點

GOF設計模式之組合設計模式(結構型模式) ✨ 每日積累

組合模式是什麼

組合模式(Composite Pattern),又叫部分整體模式,作為結構型模式,組合模式是用于把一組相似的對象當作一個單一的對象。組合多個對象形成樹形結構來表示“整體-部分”的關系層次,它建立了對象組的樹形結構。

組合模式能幹什麼

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

使用場景

部分、整體場景,如樹形菜單,檔案、檔案夾的管理。

組合模式優缺點

優點

1、高層子產品調用簡單:組合模式使得用戶端代碼可以一緻地處理單個對象群組合對象,無須關心自己處理的是單個對象,還是組合對象,這簡化了用戶端代碼;

2、節點自由增加:更容易在組合體内加入新的對象,用戶端不會因為加入了新的對象而更改源代碼,

缺點

1、在使用組合模式時,其葉子和樹枝的聲明都是實作類,而不是接口,違反了依賴倒置原則。

2、設計較複雜,用戶端需要花更多時間理清類之間的層次關系;

3、不容易限制容器中的構件;

組合模式的結構

  1. 抽象構件(Component)角色:它的主要作用是為樹葉構件和樹枝構件聲明公共接口,并實作它們的預設行為。在透明式的組合模式中抽象構件還聲明通路和管理子類的接口;在安全式的組合模式中不聲明通路和管理子類的接口,管理工作由樹枝構件完成。(總的抽象類或接口,定義一些通用的方法,比如新增、删除)
  2. 樹葉構件(Leaf)角色:是組合中的葉節點對象,它沒有子節點,用于繼承或實作抽象構件。
  3. 樹枝構件(Composite)角色 / 中間構件:是組合中的分支節點對象,它有子節點,用于繼承和實作抽象構件。它的主要作用是存儲和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。

輔助圖示

樹狀輔助圖

合模式一般用來描述整體與部分的關系,它将對象組織到樹形結構中,頂層的節點被稱為根節點,根節點下面可以包含樹枝節點和葉子節點,樹枝節點下面又可以包含樹枝節點和葉子節點,樹形結構圖如下。

GOF設計模式之組合設計模式(結構型模式) ✨ 每日積累

示例代碼圖示

組合模式的關鍵是定義了一個抽象構件類,它既可以代表葉子,又可以代表容器,而用戶端針對該抽象構件類進行程式設計,無須知道它到底表示的是葉子還是容器,可以對其進行統一處理。
GOF設計模式之組合設計模式(結構型模式) ✨ 每日積累

示例代碼

1、抽象構件

public abstract class Component {
    abstract void add(Component c);
    abstract void remove(Component c);
    abstract Component getChild(int i);
    abstract int getNumber();
    abstract void operation();
}
           

2、容器節點

import java.util.ArrayList;

public class CompositeEmployee extends Component{
    private String name;
    private String dept;
    private String position;
    private int salary;
    private ArrayList<Component> componentArrayList;


    public CompositeEmployee(String name, String dept, String position, int salary) {
        this.name = name;
        this.dept = dept;
        this.position = position;
        this.salary = salary;
        this.componentArrayList = new ArrayList<>(16);
    }

    @Override
    void add(Component c) {
        if (!componentArrayList.contains(c)) componentArrayList.add(c);
    }

    @Override
    void remove(Component c) {
        if (componentArrayList.contains(c)) componentArrayList.remove(c);
    }

    @Override
    Component getChild(int i) {
        return componentArrayList.get(i) == null ? null : componentArrayList.get(i);
    }

    @Override
    int getNumber() {
        return componentArrayList.size();
    }

    /**
     * 在組合模式結構中,由于容器構件中仍然可以包含容器構件,
     * 是以在對容器構件進行處理時需要使用遞歸算法,
     * 即在容器構件的operation()方法中遞歸調用其成員構件的operation()方法。
     */
    @Override
    void operation() {
        if (position.equals("CEO")) this.salary += 2000;
        for (Object obj : componentArrayList) {
            Component component = (Component) obj;
            if(component instanceof CompositeEmployee) {
                ((CompositeEmployee) component).setSalary(((CompositeEmployee) component).getSalary() + 1000);
                ((Component) obj).operation();
            }else {
                ((LeafEmployee) component).setSalary(((LeafEmployee) component).getSalary() + 500);
            }
            //do somthin
        }
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDept() {
        return dept;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return ("[ 姓名 : "+ name
                +", 所屬部門 : "+ dept + ", " +
                "職位 :"+ position + ", " +
                "薪水 :" + salary+" ]");
    }
}
           

3、葉子節點

import java.io.PrintStream;

public class LeafEmployee extends Component{
    private String name;
    private String dept;
    private String position;
    private int salary;

    public LeafEmployee(String name, String dept, String position, int salary) {
        this.name = name;
        this.dept = dept;
        this.position = position;
        this.salary = salary;
    }

    @Override
    void add(Component c) {
        //do somthing
    }

    @Override
    void remove(Component c) {
        //do somthing
    }

    @Override
    Component getChild(int i) {
        //do somthing
        return null;
    }

    @Override
    int getNumber() {
        //do somthing
        return 0;
    }

    @Override
    void operation() {
        //do somthing
        System.out.printf("我是員工 %d, 所屬部門 %d, 薪水是 %d", name, dept, salary);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDept() {
        return dept;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return (" [ 姓名 : "+ name
                +", 所屬部門 : "+ dept + ", " +
                "職位 :"+ position + ", " +
                "薪水 :" + salary+" ]");
    }
}
           

4、Client

public class TestClient {
    public static void main(String[] args) {
        //定義CEO
        Component componentCEO =
                new CompositeEmployee("John", "", "CEO", 40000);
        //定義TeamLeader
        Component componentTeamLealderOfJavaBackend =
                new CompositeEmployee("Robert", "JavaBackend", "TL", 25000);
        Component componentTeamLealderOfFrontEnd =
                new CompositeEmployee("Michel", "FrontEnd", "TL",25000);
        //定義普通員工
        Component componentFrontEndEmployee1 = new LeafEmployee("Bob", "FrontEnd", "Employee", 8000);
        Component componentFrontEndEmployee2 = new LeafEmployee("Laura", "FrontEnd",  "Employee",9000);

        Component componentJavaBackend1 = new LeafEmployee("Richard", "JavaBackend",  "Employee",9000);
        Component componentJavaBackend2 = new LeafEmployee("Rob", "JavaBackend",  "Employee",9000);

        //對員工進行裝載
        componentCEO.add(componentTeamLealderOfJavaBackend);
        componentCEO.add(componentTeamLealderOfFrontEnd);

        componentTeamLealderOfJavaBackend.add(componentJavaBackend1);
        componentTeamLealderOfJavaBackend.add(componentJavaBackend2);

        componentTeamLealderOfFrontEnd.add(componentFrontEndEmployee1);
        componentTeamLealderOfFrontEnd.add(componentFrontEndEmployee2);



        //列印所有員工
        System.out.println(componentCEO);
        for (int i = 0; i < componentCEO.getNumber(); i++) {
            System.out.println(componentCEO.getChild(i));
            for (int i1 = 0; i1 < componentCEO.getChild(i).getNumber(); i1++) {
                System.out.println(componentCEO.getChild(i).getChild(i1));
            }
        }

        System.out.println("----------------------------------------------");
        componentCEO.operation();
        //為所有員工調薪一次
        System.out.println(componentCEO);
        for (int i = 0; i < componentCEO.getNumber(); i++) {
            System.out.println(componentCEO.getChild(i));
            for (int i1 = 0; i1 < componentCEO.getChild(i).getNumber(); i1++) {
                System.out.println(componentCEO.getChild(i).getChild(i1));
            }
        }
    }
}
           

運作結果

[ 姓名 : John, 所屬部門 : , 職位 :CEO, 薪水 :40000 ]
[ 姓名 : Robert, 所屬部門 : JavaBackend, 職位 :TL, 薪水 :25000 ]
 [ 姓名 : Richard, 所屬部門 : JavaBackend, 職位 :Employee, 薪水 :9000 ]
 [ 姓名 : Rob, 所屬部門 : JavaBackend, 職位 :Employee, 薪水 :9000 ]
[ 姓名 : Michel, 所屬部門 : FrontEnd, 職位 :TL, 薪水 :25000 ]
 [ 姓名 : Bob, 所屬部門 : FrontEnd, 職位 :Employee, 薪水 :8000 ]
 [ 姓名 : Laura, 所屬部門 : FrontEnd, 職位 :Employee, 薪水 :9000 ]
----------------------------------------------
[ 姓名 : John, 所屬部門 : , 職位 :CEO, 薪水 :42000 ]
[ 姓名 : Robert, 所屬部門 : JavaBackend, 職位 :TL, 薪水 :26000 ]
 [ 姓名 : Richard, 所屬部門 : JavaBackend, 職位 :Employee, 薪水 :9500 ]
 [ 姓名 : Rob, 所屬部門 : JavaBackend, 職位 :Employee, 薪水 :9500 ]
[ 姓名 : Michel, 所屬部門 : FrontEnd, 職位 :TL, 薪水 :26000 ]
 [ 姓名 : Bob, 所屬部門 : FrontEnd, 職位 :Employee, 薪水 :8500 ]
 [ 姓名 : Laura, 所屬部門 : FrontEnd, 職位 :Employee, 薪水 :9500 ]