秒懂设计模式之组合模式(Composite Pattern)

Posted ShuSheng007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秒懂设计模式之组合模式(Composite Pattern)相关的知识,希望对你有一定的参考价值。

[版权申明] 非商业目的注明出处可自由转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/116378002
出自:shusheng007

设计模式汇总篇,一定要收藏:

永不磨灭的设计模式(有这一篇真够了,拒绝标题党)

概述

组合模式出镜率不算特别高,但是一旦出境说明这个问题如果不使用它将变得非常困难。android的View体系的设计方式就是组合模式非常经典的成功案例。

类型

结构型(structural)

难度

3颗星

定义

组合模式允许以相同的方式处理单个对象和对象的组合体

不理解不要紧,接着往下看

使用场景

  • 当你的程序结构有类似树一样的层级关系时,例如文件系统,视图树,公司组织架构等等
  • 当你要以统一的方式操作单个对象和由这些对象组成的组合对象的时候。

UML类图

在这里插入图片描述

从上图可见组合模式共有3组成部分

  • Component

抽象类,定义统一的处理操作。

  • Leaf

叶子节点,即单个对象

  • Composite

组合对象,里面持有一个List<Component>

我们使用了组合模式中所谓的透明方式,因为我们将单个对象和组合对象按照完全一样的事物对待了,所以接口对外很透明。统一操作都是在Component中定义的,所有继承至它的节点都要实现,而有些操作叶子节点是不支持的,例如添加移除节点等,这样就要求叶子节点处理好这些方法。

实例

林蛋大最近比较烦,公司新接了个外包IT项目,甲方是传统行业的老板,总是感觉这么简单的功能为什么要那么多钱,关键还慢…这不,刚刚要求蛋大写一个"小软件"用来管理公司的组织架构…

蛋大本来还想沟通下需求:王老板,这个活可大可小,您是不是想要…巴拉巴拉。把王老板烦的啊:小林啊,你别说了,我给你3天时间你去先做一个我看看,不行就再改嘛…。蛋大在心中怒了:老子姓楚!名中天… 做你mlgb,需求不给我做你mlgb… 但最后还是陪着笑脸说那我试试吧…

蛋大找王二狗帮忙出了个设计方案,此案例使用组合模式可解

第一,设计一个个体与组合通用的接口

定义对外展示的统一处理接口

public abstract class OrganizationComponent {
    private String name;

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

    public String getName() {
        return name;
    }

    public abstract void add(OrganizationComponent organization);

    public abstract OrganizationComponent getChild(String orgName);

    public abstract int getStaffCount();

    @Override
    public String toString() {
        return name;
    }
}

第二,组合类

此类持有一个List<OrganizationComponent>,并继承OrganizationComponent

public class OrganizationComposite extends OrganizationComponent {

    //很关键,这体现了组合的思想
    private List<OrganizationComponent> organizations = new ArrayList<>();

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

    @Override
    public void add(OrganizationComponent organization) {
        organizations.add(organization);
    }

    @Override
    public OrganizationComponent getChild(String orgName) {
        for (OrganizationComponent org : organizations) {
            OrganizationComponent targetOrg = org.getChild(orgName);
            if (targetOrg != null) {
                return targetOrg;
            }
        }
        return null;
    }

    @Override
    public int getStaffCount() {
        int count = 0;
        for (OrganizationComponent organization : organizations) {
            count += organization.getStaffCount();
        }
        return count;
    }
}   

第三,叶子节点

叶子节点就是单个对象了,我们要使用合适的方式处理那些叶子节点不支持的对外接口方法。因为用户使的时候只会看到对外暴露的统一接口,他不知道此对象是叶子节点还是组合对象。

public class ItDepartment extends OrganizationComponent {

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

    @Override
    public int getStaffCount() {
        return 20;
    }

    @Override
    public void add(OrganizationComponent organization) {
        throw new UnsupportedOperationException(this.getName()+"已经是最基本部门,无法增加下属部门");
    }

    @Override
    public OrganizationComponent getChild(String orgName) {
        if(getName().equals(orgName)){
            return this;
        }
        return null;
    }
}
// 其他叶子节点类似
...

第四,客户端

我们来看看是否达到了我们的设计目的:以统一的接口操作单个对象与其组合对象。

首先构建一个组合对象。模拟构建一家公司,公司下设行政部门和IT部门,还有一个天津分公司,而天津分公司又下设一个行政部门和IT部门。

然后我确定查询这个公司任何部门的员工人数。我们可以看到在查询过程中,我们没有判断当前对象到底是什么部门对象,都是以统一的接口在操作。

public class CompositeClient {

    private OrganizationComponent constructOrganization() {
        //构建总部
        OrganizationComposite head = new OrganizationComposite("总公司");
        AdminDepartment headAdmin = new AdminDepartment("总公司行政部");
        ItDepartment headIt = new ItDepartment("总公司It部");
        head.add(headAdmin);
        head.add(headIt);

        //构建分公司
        OrganizationComposite branch1 = new OrganizationComposite("天津分公司");
        AdminDepartment branch1Admin = new AdminDepartment("天津分公司行政部");
        ItDepartment branch1It = new ItDepartment("天津分公司It部");
        branch1.add(branch1Admin);
        branch1.add(branch1It);

        //将分公司加入到head中
        head.add(branch1);

        return head;
    }

    public void listOrgInfo() {
        OrganizationComponent org = constructOrganization();
        System.out.println(String.format("%s共有%d名员工",
                org.getName(), org.getStaffCount()));

        OrganizationComponent subOrg = org.getChild("天津分公司行政部");
        System.out.println(String.format("%s共有%d名员工",
                subOrg.getName(), subOrg.getStaffCount()));
    }
}

输出:

总公司共有140名员工
天津分公司行政部共有50名员工

技术要点总结

  • 组合模式有所谓的透明方式安全方式,是对于使用者而言的

透明方式将所有对外操作都放在Component,叶子节点也得提供这些接口,虽然它实际上不支持这些操作。而安全方式只将叶子节点与组合对象同时提供的操作放在Component

为啥叫透明方式呢?因为用户使用的时候根本不管是叶子节点,还是组合对象,反正看到的接口都一样。这样就不安全了,因为万一这个对象是个叶子节点,假设你又使用了一个它不能提供的操作,例如add,就出问题了…

  • 通用操作定义在Component中,根据使用方式不同,透明方式与安全方式,有一定的不同
  • 组合节点Composite不仅要继承Component,而且要持有一个Component的集合
  • 叶子对象只继承Component即可

优缺点

优点

  • 可以递归组合成任意复杂的对象
  • 可随意增加新类型的CompositeLeaf的类
  • 简化了客户端代码,因为不论对象多么复杂客户端都是以同一套接口操作

缺点

  • 如果需要确定某个组件是特殊组织,然后针对它做特殊的操作,就需要在运行时判断。
  • 类多了

总结

设计模式值得你可以练习!

最后,如果你从本文中有所收获,可否点赞转发支持一下博主,你小小的鼓励,是激发博主持续写作的动力…

GitHub源码地址design-patterns

以上是关于秒懂设计模式之组合模式(Composite Pattern)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之Composite模式(笔记)

设计模式之组合模式(Composite)

设计模式之组合模式 Composite

设计模式之八:组合模式(Composite Pattern)

GOF23设计模式之组合模式(composite)

java设计模式之Composite Pattern(组合模式)