我所理解的设计模式系列·第10篇·ERP系统中组合模式的应用

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

今天要跟大家聊的是组合模式(Component Pattern),需要强调的是:这里说的组合与之前在桥接模式中就提到过“以组合代替继承”思想中的“组合”不是一个概念。后者是指将A类变成B类的属性,以此来实现B类获得A类的能力的行为,而前者则是将相似对象组合成一组可被调用的树形数据结构的一种设计思想。

所以组合模式也叫合成模式,或者部分-整体模式(Part-Whole),它主要是用来描述部分与整体的关系,其目的是使得客户端在操作整体与部分对象时有统一的响应,让客户端对单个对象和组合对象的使用具有一致性,最终达到客户端与对象复杂的层次结构接耦的目的。

下面就展开说说组合模式。

design-patttern-10-组合模式-封面

1. 定义

组合模式是一种结构型设计模式,它的定义是:“将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

2. 类图

在组合模式中,有这样几种角色:

  • 抽象组件角色(Component):整体对象与部分对象的抽象,描述了整体对象与部分对象具有的统一标准。
  • 叶子角色(Leaf):不包含子节点的具体组件,组合模式中遍历的最小单位
  • 树枝,组合角色(Composite):包含了多个子节点的整体的具体组件

组合模式的类图如下:

design-patttern-10-组合模式-1-类图

3. 示例

组合模式常用于解决树形数据结构的复杂场景,比如在企业最常用的 ERP 系统中有描述组织关系的通用业务就是组合模式的最佳实践。这里其实有很多种业务场景,如公司(总公司与子公司)、部门(大部门与子部门)、组织(大组织与子组织)、员工(部门主管与部门员工)等等。

这里我们就以组织为例来说一说组合模式在 ERP 系统中的应用。需求是这样的:在一个公司的组织架构中,通常在公司下面会以业务部门为单位划分为多个组织,如负责管理公司人员的人力资源部、负责技术产品研发的技术部,而技术部下面又可以细分为前端部、后端部、测试部等,人力资源部下面又可以分为 HR 部、行政部等。

从一个公司的组织架构就可以看出它的野心、格局有多大。

对于上面提到的一个公司的所有组织,都可以用一个抽象的类来描述,即抽象组件角色,其代码如下:

public abstract class Organization {
    /**
     * 子节点集合
     */
    protected List<Organization> children = new ArrayList<>();
    /**
     * 组织名称
     */
    private String name;
    /**
     * 组织描述
     */
    private String description;

    public Organization(String name, String description) {
        this.name = name;
        this.description = description;
    }

    // 抽象方法:新增子节点
    public abstract void add(Organization organization);

    // 抽象方法:删除子节点
    public abstract void remove(Organization organization);

    // 抽象方法:获取子节点
    public abstract void getChildren();


    @Override
    public String toString() {
        return "组织名称: " + name + ", 组织描述: " + description;
    }
}
复制代码

然后创建组合模式中的另外两种角色——叶子角色与树枝角色,其代码如下:

public class ParentOrganization extends Organization {

    public ParentOrganization(String name, String description) {
        super(name, description);
    }

    @Override
    public void add(Organization organization) {
        this.children.add(organization);
    }

    @Override
    public void remove(Organization organization) {
        this.children.remove(organization);
    }

    @Override
    public void getChildren() {
        for (Organization organization : this.children) {
            if (organization instanceof ParentOrganization) {
                organization.getChildren();
            } else {
                System.out.println(organization);
            }
        }
    }
}

public class LeafOrganization extends Organization {

    public LeafOrganization(String name, String description) {
        super(name, description);
    }

    @Override
    public void add(Organization organization) {
        throw new UnsupportedOperationException("叶子角色不允许操作 add 方法");
    }

    @Override
    public void remove(Organization organization) {
        throw new UnsupportedOperationException("叶子角色不允许操作 remove 方法");
    }

    @Override
    public void getChildren() {
        throw new UnsupportedOperationException("叶子角色不允许操作 getChildrenNumber 方法");
    }
}
复制代码

然后编写测试类,先创建一个顶层组织,即总公司,然后分别创建人力资源部、技术部及其下属部门,其代码如下:

public class Test {
    public static void main(String[] args) {
        Organization company = new ParentOrganization("设计模式控股公司", "专职研究设计模式的互联网公司");

        Organization humanResource = new ParentOrganization("人力资源部", "负责管理公司人员");
        Organization hr = new LeafOrganization("HR部", "负责招聘");
        Organization administration = new LeafOrganization("行政部", "负责建立和完善工作程序、岗位职责等");
        humanResource.add(hr);
        humanResource.add(administration);

        Organization technology = new ParentOrganization("技术部", "负责公司技术产品研发");
        Organization frontend = new LeafOrganization("前端部", "负责前端开发");
        Organization backend = new LeafOrganization("后端部", "负责后端开发");
        Organization test = new LeafOrganization("测试部", "负责产品质量保障");
        technology.add(frontend);
        technology.add(backend);
        technology.add(test);

        // 公司下属人力资源部及技术部
        company.add(humanResource);
        company.add(technology);
    }
}
复制代码

最后,对本公司进行一些测试,其代码如下:

public class Test {
    public static void main(String[] args) {
        System.out.println("设计模式控股公司下部门:");
        company.getChildren();
        System.out.println("人力资源部下部门:");
        humanResource.getChildren();
        System.out.println("技术部下部门:");
        technology.getChildren();
    }
}
复制代码

输出结果为:

设计模式控股公司下部门:
组织名称: HR部, 组织描述: 负责招聘
组织名称: 行政部, 组织描述: 负责建立和完善工作程序、岗位职责等
组织名称: 前端部, 组织描述: 负责前端开发
组织名称: 后端部, 组织描述: 负责后端开发
组织名称: 测试部, 组织描述: 负责产品质量保障
人力资源部下部门:
组织名称: HR部, 组织描述: 负责招聘
组织名称: 行政部, 组织描述: 负责建立和完善工作程序、岗位职责等
技术部下部门:
组织名称: 前端部, 组织描述: 负责前端开发
组织名称: 后端部, 组织描述: 负责后端开发
组织名称: 测试部, 组织描述: 负责产品质量保障
复制代码

4. 使用场景

组合模式的使用场景是:

  • 维护和展示“部分-整体”关系的场景,例如树形结构、文件管理
  • 从一个整体中能够独立出部分模块或功能的场景

5. 小结

本文讲述了组合模式,它的定义是——将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

组合模式的优点是:

  • 无论是组织单个叶子结点还是复杂的树状结构,对于客户端来说的调用是简单的
  • 符合开闭原则,扩展十分方便,子节点可以自由增加与删除

组合模式的缺点是:

  • 不符合依赖倒置原则,客户端直接使用了叶子节点和树枝节点

6. 参考资料

  • 《设计模式之禅》(第2版)

最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。

Guess you like

Origin juejin.im/post/7061998271920603167