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

定义

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

使用场景

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

    UML

    组合模式 - 图1

    角色结构

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

  • Leaf:叶子节点,即单个对象
  • Composite:组合对象,里面持有一个List<Component>

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

优点

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

    缺点

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

  • 类多了

    业务场景

    组织中员工的层次结构。

    代码示例

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

    1. public abstract class OrganizationComponent {
    2. private String name;
    3. public OrganizationComponent(String name) {
    4. this.name = name;
    5. }
    6. public String getName() {
    7. return name;
    8. }
    9. public abstract void add(OrganizationComponent organization);
    10. public abstract OrganizationComponent getChild(String orgName);
    11. public abstract int getStaffCount();
    12. @Override
    13. public String toString() {
    14. return name;
    15. }
    16. }

    OrganizationComposite 组合类

    1. public class OrganizationComposite extends OrganizationComponent {
    2. /*
    3. * 很关键,这体现了组合的思想
    4. */
    5. private List<OrganizationComponent> organizations = new ArrayList<>();
    6. public OrganizationComposite(String name) {
    7. super(name);
    8. }
    9. @Override
    10. public void add(OrganizationComponent organization) {
    11. organizations.add(organization);
    12. }
    13. @Override
    14. public OrganizationComponent getChild(String orgName) {
    15. for (OrganizationComponent org : organizations) {
    16. OrganizationComponent targetOrg = org.getChild(orgName);
    17. if (targetOrg != null) {
    18. return targetOrg;
    19. }
    20. }
    21. return null;
    22. }
    23. @Override
    24. public int getStaffCount() {
    25. int count = 0;
    26. for (OrganizationComponent organization : organizations) {
    27. count += organization.getStaffCount();
    28. }
    29. return count;
    30. }
    31. }

    ItDepartment 叶子节点-IT部门

    1. public class ItDepartment extends OrganizationComponent {
    2. public ItDepartment(String name) {
    3. super(name);
    4. }
    5. @Override
    6. public int getStaffCount() {
    7. return 20;
    8. }
    9. @Override
    10. public void add(OrganizationComponent organization) {
    11. throw new UnsupportedOperationException(this.getName()+"已经是最基本部门,无法增加下属部门");
    12. }
    13. @Override
    14. public OrganizationComponent getChild(String orgName) {
    15. if(getName().equals(orgName)){
    16. return this;
    17. }
    18. return null;
    19. }
    20. }

    AdministrationDepartment 叶子节点-行政部门

    1. public class AdministrationDepartment extends OrganizationComponent {
    2. public AdministrationDepartment(String name) {
    3. super(name);
    4. }
    5. @Override
    6. public int getStaffCount() {
    7. return 20;
    8. }
    9. @Override
    10. public void add(OrganizationComponent organization) {
    11. throw new UnsupportedOperationException(this.getName()+"已经是最基本部门,无法增加下属部门");
    12. }
    13. @Override
    14. public OrganizationComponent getChild(String orgName) {
    15. if(getName().equals(orgName)){
    16. return this;
    17. }
    18. return null;
    19. }
    20. }

    客户端

    1. public class Client {
    2. public static void main(String[] args) {
    3. Client client = new Client();
    4. client.listOrgInfo();
    5. }
    6. private OrganizationComponent constructOrganization() {
    7. //构建总部
    8. OrganizationComposite head = new OrganizationComposite("总公司");
    9. AdministrationDepartment headAdmin = new AdministrationDepartment("总公司行政部");
    10. ItDepartment headIt = new ItDepartment("总公司It部");
    11. head.add(headAdmin);
    12. head.add(headIt);
    13. //构建分公司
    14. OrganizationComposite branch1 = new OrganizationComposite("西安分公司");
    15. AdministrationDepartment branch1Admin = new AdministrationDepartment("西安分公司行政部");
    16. ItDepartment branch1It = new ItDepartment("西安分公司It部");
    17. branch1.add(branch1Admin);
    18. branch1.add(branch1It);
    19. //将分公司加入到head中
    20. head.add(branch1);
    21. return head;
    22. }
    23. public void listOrgInfo() {
    24. OrganizationComponent org = constructOrganization();
    25. System.out.println(String.format("%s共有%d名员工",
    26. org.getName(), org.getStaffCount()));
    27. OrganizationComponent subOrg = org.getChild("西安分公司行政部");
    28. System.out.println(String.format("%s共有%d名员工",
    29. subOrg.getName(), subOrg.getStaffCount()));
    30. }
    31. }

    输出

    1. 总公司共有80名员工
    2. 西安分公司行政部共有20名员工

    技术要点总结

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

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

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

  • 通用操作定义在Component中,根据使用方式不同,透明方式与安全方式,有一定的不同

  • 组合节点Composite不仅要继承Component,而且要持有一个Component的集合
  • 叶子对象只继承Component即可