组合模式

2019年5月23日21:50:31

组合模式(composite pattern)

定义

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。————《设计模式:可复用面向对象软件的基础》

组合模式是一个结构型模式。

使用场景

当你发现需求中是用树形结构体现部分与整体层次关系的结构时,且你希望用户可以忽略整体和部分、组合对象和单个对象的不同,统一地使用组合结构中的所有对象时,就应该使用组合模式。

组合模式解决上面所说的两个问题:

1、部分与整体的层次关系表示为树形结构、

2、部分与整体的对象,是能让客户端能统一对待、不需区分的对象。

场景:树形菜单,文件、文件夹的管理。

角色

抽象组件角色(Component):

  • 所有组件的抽象
  • 声明在组合模式中的对象的接口

叶子角色(Leaf):

  • 表示组合模式中的叶子对象
  • 实现了Component
  • 没有子节点

组合角色(Composite):

  • 表示一个组合组件(拥有叶子节点)
  • 实现了Component,
  • 拥有子节点,并具备操作Component的方法,增加组件、删除组件等

图示

组合模式结构图:

组合模式结构图

代码示例

深圳某公司总部使用的OA系统,由于简单易用,反响良好的原因,准备被推广到各分公司使用。

关于组织结构的设计,原来是这样的:一个公司总部下面有人力资源部、财务部等。组织结构图如下:

推广之前的组织结构图

类图如下:

应用组合模式前的组合结构类图

代码如下:

// 组织
public interface Organization {
    void addOrg(Department department);
    void removeOrg(Department department);
    String getName();
    void showOrg();
    void displayDuty();
}
// 公司总部
public class Headquarters implements  Organization {
    private List<Department> departments = new ArrayList<>();

    @Override
    public void addOrg(Department department) {
        departments.add(department);
    }

    @Override
    public void removeOrg(Department department) {
        departments.remove(department);
    }

    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public void showOrg() {
        System.out.println("-" + getName());
        for (Department department : departments) {
            System.out.println("--" +  department.getName());
        }
    }

    @Override
    public void displayDuty() {
        for(Department department : departments) {
            department.duty();
        }
    }
}
// 部门
public interface Department {
    String getName();
    void duty();
}
// 财务部
public class FinanceDepartment implements Department {

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public void duty() {
        System.out.println("财务部负责公司财务收支管理");
    }
}
// 人力资源部
public class HumanResourceDepartment implements Department{

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public void duty() {
        System.out.println("人力资源部负责员工招聘培训管理");
    }
}
// 测试类
public class Before {
    public static void main(String[] args) {
        Organization hq = new Headquarters();

        Department fnc = new FinanceDepartment();

        Department hr = new HumanResourceDepartment();

        hq.addOrg(fnc);
        hq.addOrg(hr);

        System.out.println("====组织结构====");
        hq.showOrg();
        System.out.println("====部门职责====");
        hq.displayDuty();
    }
}

测试结果:

应用组合模式之前的测试结果

组织结构需求现在变成了:总部下面不仅是只有部门,还会有分公司。之前的设计明显是不可以用了,接口Organization中addOrg、removeOrg方法都是面对于Department的。现在需要不区分部门、分公司和总部,且组织结构是用树形结构来体现层次关系的。组合模式就派上用场了。

推广之后组织机构图:

推广之后的组织机构图

应用组合模式的类图:

应用组合模式的类图

应用组合模式的代码:

// 公司组织或部门
public interface Company {

    void addOrg(Company company);
    void removeOrg(Company company);
    void showOrg(int depth);
    default void print(int dept) {
        while(dept-- > 0) {
            System.out.print("-");
        }
    }
}
// 总部或分公司
public class ConcreteComp implements Company {
    private String name = "";
    private List<Company> companies = new ArrayList<>();
    ConcreteComp(String name) {
        this.name = name;
    }

    @Override
    public void addOrg(Company company) {
        companies.add(company);
    }

    @Override
    public void removeOrg(Company company) {
        companies.remove(company);
    }

    @Override
    public void showOrg(int dept) {
        print(dept);
        System.out.println(this.name);
        for (Company c : companies) {
            c.showOrg(dept + 2);
        }
    }
}
// 财务部
public class FinanceDepartment implements Company {
    private String name = "";
    FinanceDepartment(String name) {
        this.name = name;
    }

    @Override
    public void addOrg(Company company) {
    }

    @Override
    public void removeOrg(Company company) {
    }

    @Override
    public void showOrg(int dept) {
        print(dept);
        System.out.println(this.name);
    }
}
// 人力资源部
public class HumanResourceDepartment implements Company {
    private String name = "";
    HumanResourceDepartment(String name) {
        this.name = name;
    }

    @Override
    public void addOrg(Company company) {
    }

    @Override
    public void removeOrg(Company company) {
    }

    @Override
    public void showOrg(int dept) {
        print(dept);
        System.out.println(this.name);
    }
}
// 测试类
public class After {
    public static void main(String[] args) {
        Company headquarters = new ConcreteComp("公司总部");
        headquarters.addOrg(new FinanceDepartment("财务部"));
        headquarters.addOrg(new HumanResourceDepartment("人力资源部"));

        Company gd = new ConcreteComp("广东分公司");
        gd.addOrg(new FinanceDepartment("财务部"));
        gd.addOrg(new HumanResourceDepartment("人力资源部"));

        Company sz = new ConcreteComp("深圳分公司");
        sz.addOrg(new FinanceDepartment("财务部"));
        sz.addOrg(new HumanResourceDepartment("人力资源部"));

        gd.addOrg(sz);

        Company sh = new ConcreteComp("上海分公司");
        sh.addOrg(new FinanceDepartment("财务部"));
        sh.addOrg(new HumanResourceDepartment("人力资源部"));

        headquarters.addOrg(gd);
        headquarters.addOrg(sh);

        headquarters.showOrg(1);

        Company test = new FinanceDepartment("测试财务部");
        test.addOrg(new HumanResourceDepartment("测试人力资源部"));
        test.addOrg(sh);

        test.showOrg(1);
    }
}

测试结果:

应用组合模式之后的测试结果

总部下可以添加分公司和部门,分公司下也可以添加分公司和部门,甚至部门下也可以添加分公司和部门,做到了不用区分整体和部分。只是部门下添加分公司和部门是不起作用的。

透明方式与安全方式

上面应用组合模式的方式就是透明方式,接口Company定义了addOrg、removeOrg方法,无论是叶子角色还是树枝角色都拥有addOrg、removeOrg方法,从实际使用的角度来说,叶子角色不需要这两个方法。对于外界来说,叶子和树枝的区别是透明的,这就是透明方式。

安全方式就是接口Company里没有定义addOrg、removeOrg方法,只在树枝角色添加addOrg、removeOrg方法。这样用户需要对叶子角色和树枝角色进行判断能不能使用ddOrg、removeOrg方法。

透明方式的缺点是叶子角色在添加叶子或者树枝时是什么也不做,用户觉得应该是操作成功了,但是并没有操作成功。安全方式的缺点就是在添加叶子和树枝的时候需要进行判断。

优点

对服务端来说,以树形结构清晰定义了整体和部分的层次关系,只需要知道自己的父节点就可以自由添加子节点。

对客户端来说,可以忽略整体和部分的差异,不需关心是单个对象还是组合对象,简化了逻辑。

总结

组合模式是将对象 组合成树形结构以表示‘部分-整体’的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

参考

《大话设计模式》

2019年7月18日20:09:21

posted on 2019-07-19 20:08  mingmingcome  阅读(798)  评论(0编辑  收藏  举报

导航