设计模式--组合模式

缘起

某日,小明公司最近接到一个办公管理系统的项目,并且在每个城市都有分部。这属于是很常见的OA系统,只要前期将需求分析完善好,中后期开发维护是不难的。

然而,总部公司使用后觉得很OK,想要其他城市的分公司也执行使用。但是现在的问题是,其他分公司的部门和制度没有总公司那么清晰完善。

也许可以一个城市一套代码?但是总公司不乐意了,要求总部、分部等是需要成树状结构的,不可以平行管理。

那么部门Leader想到了一个合适的设计模式--组合模式。

组合模式

组合模式(Composite):将对象组合成树形结构以表示'部门-整体'的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

结构图

Component:作为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为,声明一个接口用于访问和管理Component的子组件

通常使用add和remove增加或删除树枝或树叶的功能

java 复制代码
public abstract class Component {
    
    protected String name;

    public Component(String name) {
        this.name = name;
    }
    public abstract void add(Component component);
    public abstract void remove(Component component);
	public abstract void display(int depth);
}
  • Leaf在组合中表示叶节点对象,叶子节点没有子节点,所以add和remove对于Leaf对象来说是没用的,给客户端个提示就行。
java 复制代码
public class Leaf extends Component {

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

    @Override
    public void add(Component component) {
        System.out.println("Cannot add to a leaf");
    }
    @Override
    public void remove(Component component) {
        System.out.println("Cannot remove from a leaf");
    }
    
    // 叶子节点展示职级和名称
    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }
}
  • Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加add和删除remove
java 复制代码
public class Composite extends Component {

    private List<Component> children = new ArrayList<>();

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

    @Override
    public void add(Component component) {
        children.add(component);
    }

    @Override
    public void remove(Component component) {
        children.remove(component);
    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
		// 表层级关系+2
        for (Component item : children) {
            item.display(depth + 2);
        }
    }
}
  • 客户端调用,可以通过Component接口操作组合部件的对象
java 复制代码
// 顶级节点
Composite root = new Composite("root");
// 添加两个叶子节点
root.add(new Leaf("Leaf A"));
root.add(new Leaf("Leaf B"));

Composite comp = new Composite("Composite X");
// 加两个叶子节点
comp.add(new Leaf("Leaf A"));
comp.add(new Leaf("Leaf B"));

// 顶级节点加一个枝节点
root.add(comp);

Composite comp2 = new Composite("Composite XY");
// 加两个叶子节点
comp.add(new Leaf("Leaf XYA"));
comp.add(new Leaf("Leaf XYB"));

// 二级节点再加一个枝节点
comp.add(comp2);

// 展示整个架构
root.display(1);

结果

txt 复制代码
-root
---Leaf A
---Leaf B
---Composite X
-----Leaf A
-----Leaf B
-----Leaf XYA
-----Leaf XYB
-----Composite XY

当发现需求中是体现部分与整体层次的结构时,希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中的所有对象时,就应该考虑使用组合模式了。

在java开发窗体应用的容器控件Container继承了Component(java自带的),就有add和remove方法,所以它在上面增加控件,如Button、Label、Checkbox等就变成了很自然的事,这就是典型的组合模式的应用。

公司管理系统案例

公司类:抽象类或接口

java 复制代码
public abstract class Company {
    protected String name;

    public Company(String name) {
        this.name = name;
    }
    
    public abstract void add(Company company);  // 增加
    public abstract void remove(Company company); // 删除
    public abstract void display(int depth);    // 展示
    public abstract void lineOfDuty();  // 展示职责
    
}

具体公司类:实现接口、树枝节点

java 复制代码
public class ConcreteCompany extends Company {

    protected List<Company> children = new ArrayList<>();
    public ConcreteCompany(String name) {
        super(name);
    }

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

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

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);

        for (Company child : children) {
            child.display(depth + 2);
        }
    }

    @Override
    public void lineOfDuty() {
        for (Company child : children) {
            child.lineOfDuty();
        }
    }
}
  • 人力资源部、财务部:叶子节点
java 复制代码
// HR
public class HRDept extends Company {
    public HRDept(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {

    }

    @Override
    public void remove(Company company) {

    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }

    @Override
    public void lineOfDuty() {
        System.out.println(name+"员工招聘培训");
    }
}
// 财务部
public class FinanceDept extends Company {

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

    @Override
    public void add(Company company) {

    }

    @Override
    public void remove(Company company) {

    }

    @Override
    public void display(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("-");
        }
        System.out.println(name);
    }

    @Override
    public void lineOfDuty() {
        System.out.println(name + "公司财务收支管理");
    }
}
  • 客户端
java 复制代码
// 顶级节点
ConcreteCompany root = new ConcreteCompany("总公司");
root.add(new HRDept("总公司人力资源"));
root.add(new FinanceDept("总公司财务部"));


ConcreteCompany comp = new ConcreteCompany("某分公司");
comp.add(new HRDept("某分公司人力资源"));
comp.add(new FinanceDept("某分公司财务部"));

// 给总公司增加下级部门
root.add(comp);

ConcreteCompany comp2 = new ConcreteCompany("A办事处");
comp2.add(new HRDept("A办事处人力资源"));
comp2.add(new FinanceDept("A办事处财务部"));
comp.add(comp2);

ConcreteCompany comp3 = new ConcreteCompany("B办事处");
comp3.add(new HRDept("B办事处人力资源"));
comp3.add(new FinanceDept("B办事处财务部"));

comp.add(comp3);
System.out.println("结构图");
root.display(1);

System.out.println("职责");
root.lineOfDuty();
txt 复制代码
结构图
-总公司
---总公司人力资源
---总公司财务部
---某分公司
-----某分公司人力资源
-----某分公司财务部
-----A办事处
-------A办事处人力资源
-------A办事处财务部
-----B办事处
-------B办事处人力资源
-------B办事处财务部
职责
总公司人力资源员工招聘培训
总公司财务部公司财务收支管理
某分公司人力资源员工招聘培训
某分公司财务部公司财务收支管理
A办事处人力资源员工招聘培训
A办事处财务部公司财务收支管理
B办事处人力资源员工招聘培训
B办事处财务部公司财务收支管理
相关推荐
lxyzcm11 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千11 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)12 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
诸葛悠闲13 小时前
设计模式——桥接模式
设计模式·桥接模式
捕鲸叉17 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~18 小时前
框架专题:设计模式
设计模式·框架
先睡18 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
Damon_X1 天前
桥接模式(Bridge Pattern)
设计模式·桥接模式
越甲八千1 天前
重温设计模式--享元模式
设计模式·享元模式
码农爱java1 天前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式