什么是组合模式
组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
组合模式要解决的关键问题就是使得用户对单个对象和组合对象访问具有一致性。
场景
前面只有定义没有实际场景,下面我们就用实际的场景来说明。例如现在有个需求需要你来统计公司员工的薪水,对于员工来说,薪水总和就是当前员工的个人薪资。但是对于部门来说,那就是部门下所有员工的薪水。如果部门下面还有小组,那么那还得统计整个小组的员工薪水总和。

从上图可以看出,公司整个架构就是一个树形结构,如果我们抽象成对象的话可以抽象成两类,第一类就是容器类,例如部门、小组等,这类它可以包含多个个体。另一类就是个体类,这里面CEO、小明、小花等员工都是,此类就是容器类的子节点。如果让你用代码实现,你可以会定义两类接口,分别用来表示容器类和单体类,公司、部门和小组实现容器类接口,而员工和CEO则实现单体类接口。

按照这个设计,客户端在操作容器类和单体类时需要区别对待。
对象分析
上面的例子很适合使用组合模式来解决,在组合模式中一般存在以下几种对象:
抽象组件对象(Component)
该对象为一般为接口或抽象类且声明了组件的相关方法,客户端就是通过该接口来管理和访问对象的。
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
/**
* 添加组件
* @param component
*/
public void add(Component component){
throw new UnsupportedOperationException();
}
/**
* 删除组件
* @param component
*/
public void remove(Component component){
throw new UnsupportedOperationException();
}
/**
* 获取薪水
* @return
*/
public abstract Integer getSalary();
/**
* 打印
* @param level
*/
public abstract void print(Integer level);
}
组合对象(Composite)
该对象为抽象组件的子类通常会用来存储子组件,实现抽象组件中管理子组件的相关方法。
public class Composite extends Component{
private List<Component> components = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void add(Component component) {
components.add(component);
}
@Override
public void remove(Component component) {
components.add(component);
}
@Override
public Integer getSalary() {
int sum = 0;
for (Component component : components) {
sum = sum + component.getSalary();
}
return sum;
}
@Override
public void print(Integer level) {
for (int i = 0; i < level; i++) {
System.out.print("-");
}
System.out.println(name);
components.forEach(component -> component.print(level+1));
}
}
叶子节点对象(Leaf)
该对象同样为抽象组件的子类,但是一般不会实现管理子组件的相关方法。
public class Leaf extends Component{
private Integer salary;
public Leaf(String name, Integer salary) {
super(name);
this.salary = salary;
}
@Override
public Integer getSalary() {
return salary;
}
@Override
public void print(Integer level) {
for (int i = 0; i < level; i++) {
System.out.print("-");
}
System.out.println(name + "(" + salary + ")");
}
}
客户端调用
上面已经介绍了组合模式中的各种对象,那么客户端该如何调用呢?
public class App {
public static void main(String[] args) {
//公司
Component company = new Composite("公司");
//产品部
Component department1 = new Composite("产品部");
company.add(department1);
//水机组
Component group1 = new Composite("水机组");
group1.add(new Leaf("小明",1000));
group1.add(new Leaf("小白",1000));
department1.add(group1);
//食品组
Component group2 = new Composite("食品组");
group2.add(new Leaf("小黑",2000));
department1.add(group2);
//养生壶组
Component group3 = new Composite("养生壶组");
group3.add(new Leaf("小花",2000));
department1.add(group3);
//技术部
Component department2 = new Composite("技术部");
company.add(department2);
//前端组
Component group4 = new Composite("前端组");
group4.add(new Leaf("张三",2000));
department2.add(group4);
//后端组
Component group5 = new Composite("后端组");
group5.add(new Leaf("李四",2000));
group5.add(new Leaf("王麻子",2000));
department2.add(group5);
//测试组
Component group6 = new Composite("测试组");
group6.add(new Leaf("王五",2000));
department2.add(group6);
//CEO
company.add(new Leaf("CEO",10000));
//打印
company.print(0);
//计算薪水
Integer salary = company.getSalary();
System.out.println(salary);
}
}
运行代码,打印结果如下:
公司
-产品部
--水机组
---小明(1000)
---小白(1000)
--食品组
---小黑(2000)
--养生壶组
---小花(2000)
-技术部
--前端组
---张三(2000)
--后端组
---李四(2000)
---王麻子(2000)
--测试组
---王五(2000)
-CEO(10000)
24000
总结
组合模式定义了包含基本对象和层次结构,基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去在客户端代码中,任何用到基本对象的地方都可以使用组合对象了,客户端不用去关心到底处理的是叶子节点还是组合组件。简单的说,组合模式就是让客户端可以一致地使用组合结构和单个对象。它的优点如下:
-
组合模式清晰的定义了包含基本对象和组合对象的类层次结构。 -
客户端不用关心处理的是基本对象还是组合对象。 -
在组合模式中,增加新的叶子构建和容器构件会很方便,无需对类进行修改,符合开闭原则
而它的缺点如下:
-
无法限制组合组件中的子组件类型。在需要检测组件类型时,不能依靠编译期的类型约束来实现,必须在运行期间动态检测。
本文示例代码地址:https://gitee.com/zengchao_workspace/design-pattern
原文始发于微信公众号(一只菜鸟程序员):设计模式(六)-组合模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72885.html