GOF设计模式之组合设计模式(结构型模式) ✨ 每日积累

简介: GOF设计模式之组合设计模式(结构型模式) ✨ 每日积累

组合模式是什么


组合模式(Composite Pattern),又叫部分整体模式,作为结构型模式,组合模式是用于把一组相似的对象当作一个单一的对象。组合多个对象形成树形结构来表示“整体-部分”的关系层次,它创建了对象组的树形结构。


组合模式能干什么


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


使用场景


部分、整体场景,如树形菜单,文件、文件夹的管理。


组合模式优缺点


优点

1、高层模块调用简单:组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;


2、节点自由增加:更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,


缺点

1、在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。


2、设计较复杂,客户端需要花更多时间理清类之间的层次关系;


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


组合模式的结构

抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)

树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。

树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。


辅助图示


树状辅助图

合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下。

1.png

示例代码图示

组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。

1.png


示例代码


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 ]
相关文章
|
3月前
|
设计模式 存储 Java
【十】设计模式~~~结构型模式~~~享元模式(Java)
文章详细介绍了享元模式(Flyweight Pattern),这是一种对象结构型模式,通过共享技术实现大量细粒度对象的重用,区分内部状态和外部状态来减少内存中对象的数量,提高系统性能。通过围棋棋子的设计案例,展示了享元模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了单纯享元模式和复合享元模式以及与其他模式的联用。
【十】设计模式~~~结构型模式~~~享元模式(Java)
|
3月前
|
设计模式 存储 Java
【九】设计模式~~~结构型模式~~~外观模式(Java)
文章详细介绍了外观模式(Facade Pattern),这是一种对象结构型模式,通过引入一个外观类来简化客户端与多个子系统之间的交互,降低系统的耦合度,并提供一个统一的高层接口来使用子系统。通过文件加密模块的实例,展示了外观模式的动机、定义、结构、优点、缺点以及适用场景,并讨论了如何通过引入抽象外观类来提高系统的可扩展性。
【九】设计模式~~~结构型模式~~~外观模式(Java)
|
3月前
|
设计模式 Java
【八】设计模式~~~结构型模式~~~装饰模式(Java)
文章详细介绍了装饰模式(Decorator Pattern),这是一种对象结构型模式,用于在不使用继承的情况下动态地给对象添加额外的职责。装饰模式通过关联机制,使用装饰器类来包装原有对象,并在运行时通过组合的方式扩展对象的行为。文章通过图形界面构件库的设计案例,展示了装饰模式的动机、定义、结构、优点、缺点以及适用场景,并提供了Java代码实现和应用示例。装饰模式提高了系统的灵活性和可扩展性,适用于需要动态、透明地扩展对象功能的情况。
【八】设计模式~~~结构型模式~~~装饰模式(Java)
|
3月前
|
设计模式 XML 存储
【七】设计模式~~~结构型模式~~~桥接模式(Java)
文章详细介绍了桥接模式(Bridge Pattern),这是一种对象结构型模式,用于将抽象部分与实现部分分离,使它们可以独立地变化。通过实际的软件开发案例,如跨平台视频播放器的设计,文章阐述了桥接模式的动机、定义、结构、优点、缺点以及适用场景,并提供了完整的代码实现和测试结果。桥接模式适用于存在两个独立变化维度的系统,可以提高系统的可扩展性和灵活性。
【七】设计模式~~~结构型模式~~~桥接模式(Java)
|
6月前
|
设计模式 存储 安全
Java设计模式---结构型模式
Java设计模式---结构型模式
|
3月前
|
设计模式 XML 存储
【六】设计模式~~~结构型模式~~~适配器模式(Java)
文章详细介绍了适配器模式(Adapter Pattern),这是一种结构型设计模式,用于将一个类的接口转换成客户期望的另一个接口,使原本不兼容的接口能够一起工作,提高了类的复用性和系统的灵活性。通过对象适配器和类适配器两种实现方式,展示了适配器模式的代码应用,并讨论了其优点、缺点以及适用场景。
|
3月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
6月前
|
设计模式 Java 应用服务中间件
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
59 1
|
5月前
|
设计模式
设计模式之结构型模式
设计模式之结构型模式
|
6月前
|
设计模式 缓存 监控
JAVA设计模式之结构型模式
结构模型:适配器模型、桥接模型、过滤器模型、组合模型、装饰器模型、外观模型、享受元模型和代理模型。
68 3