Java设计模式-组合模式(Composite Pattern)
目录
- 什么是组合模式
- 组合模式的2种类型
- JavaSE组合模式的应用
- Struts2组合模式的应用
“将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。”
一、什么是组合模式
组合模式又成为整体-部分模式,它是将对象组合成树状结构的层次结构的模式,大家想一下大学学习的二叉树就能理解了,有树枝节点与叶子节点,例如下面的图,是不是很容易理解了
树枝和叶子的区别是是否有子节点,树枝中应该知道自己有哪些子节点,这种形式涉及到三个角色:
- **抽象构件(Component)角色:**这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。组合对象通常把它所包含的子对象当做类型为
Component的对象。在安全式的组合模式里,构建角色并不定义出管理子对象的方法,这一定义由树枝构件对象给出。 - **树叶构件(Leaf)角色:**树叶对象没有下级子对象的对象,定义出参加组合的原始对象的行为。
- **树枝构件(Composite)角色:**代表参加组合的有下级子对象的对象。树枝构件类给出所有的管理子对象的方法,如
add()、remove()、以及getChild()等。
二、组合模式的2种类型
上面提到树枝和叶子的区别是是否有子节点,而如何管理子节点将是他们的区别,是让叶子和树枝都有管理子节点的能力,还是只让树枝有管理子节点的能力,这种理念的区别造成组合模式有2种:
- 安全式组合模式:安全模式的组合模式要求管理子节点的方法只出现在树枝构件类中,而不出现在树叶构件类中
- 透明式组合模式:透明式的组合模式要求所有的具体构建类,不论树枝构件还是树叶构件,均符合一个固定接口
第一张图是安全模式,第二张是透明模式;透明模式的Leaf类的printStruct()是空方法或者接近空方法的简单实现。
为什么叫安全模式呢?是从客户端角度看是否安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)
透明模式是Component生命力所有的方法,哪怕Leaf不会用到getChild,同样也需要实现,对所有子类方法都是很透明的,所以叫透明模式。
两种模式各有优点,安全模式不会出现误操作,减少未知问题;透明模式对客户端透明,无需关心类型,以统一的方式操作,更加符合组合模式的定义:【用户对单个对象和组合对象的使用具有一致性】
用哪种模式都可以,我们以安全模式举例,先看下UML图
代码如下
package org.CompositePattern.version1;
import java.util.ArrayList;
import java.util.List;
/**
* 树枝与树叶的抽象类
*/
abstract class Component{
private String name;
private String position;
private int salary;
public Component(String name, String position, int salary){
this.name = name;
this.position = position;
this.salary = salary;
}
public String getInfo(){
String res = "姓名:" + this.name + "," + "职位:" + this.position + "," + "薪水:" + this.salary;
System.out.println(res);
return res;
}
}
class Leaf extends Component{
public Leaf(String name, String position, int salary) {
super(name, position, salary);
}
}
/**
* 树枝类,也就是节点类,内部有添加树叶,获取树叶信息的方法
*/
class Branch extends Component{
List<Component> staffList = new ArrayList<>();
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
public void addStaff(Component component){
this.staffList.add(component);
}
public List<Component> getStaffs(){
for (Component com:this.staffList ) {
com.getInfo();
// 如果是树枝,则获取树枝下的叶子
if (com instanceof Branch){
((Branch) com).getStaffs();
}
}
return this.staffList;
}
}
public class Test {
public static void main(String[] args) {
Branch ceo = init();
// CEO要知道自己所有员工的信息
ceo.getStaffs();
}
// 准备数据,一般来自数据库
public static Branch init(){
Leaf zhangsan = new Leaf("张三","员工",6000);
Leaf lisi = new Leaf("李四","员工",5000);
Leaf wangwu = new Leaf("王五","员工",4000);
Branch dev = new Branch("张明","研发部领导",20000);
dev.addStaff(zhangsan);
dev.addStaff(lisi);
dev.addStaff(wangwu);
Leaf liuning = new Leaf("刘宁","员工",6000);
Branch sell = new Branch("王伟","销售部领导",20000);
sell.addStaff(liuning);
Branch ceo = new Branch("陈真","CEO",60000);
ceo.addStaff(dev);
ceo.addStaff(sell);
return ceo;
}
}
// 运行结果
姓名:张明,职位:研发部领导,薪水:20000
姓名:张三,职位:员工,薪水:6000
姓名:李四,职位:员工,薪水:5000
姓名:王五,职位:员工,薪水:4000
姓名:王伟,职位:销售部领导,薪水:20000
姓名:刘宁,职位:员工,薪水:6000
三、JavaSE组合模式的应用
HashMap使用了组合模式,分析下类的关系
- **抽象构件(Component)角色:**Map
- **树叶构件(Leaf)角色:**Node
- **树枝构件(Composite)角色:**HashMap
HashMap是一个树枝构件,定义了数据的存储方式,内部定义了静态内部类的数组Node<K,V>[] tab,用于存储数据,HashMap有如下方法
- put,加入一个叶子节点
- remove,删除一个叶子节点
- get,获取一个节点信息
看个案例
package org.CompositePattern.version2;
import java.util.HashMap;
class People{
private String name;
private int age;
public People(String name, int age){
this.name = name;
this.age = age;
}
}
public class TestHashMap {
public static void main(String[] args) {
HashMap ceo = new HashMap();
// 开发部
HashMap dev = new HashMap();
dev.put("zhangsan",new People("zhangsan",20));
dev.put("lisi",new People("lisi",20));
// 销售部
HashMap sell = new HashMap();
sell.put("zhangsan",new People("zhangsan",20));
sell.put("lisi",new People("lisi",20));
//老板管2个部门
ceo.put("dev",dev);
ceo.put("sell",sell);
}
}
HashMap维护了和前面案例类似的组织架构
四、Struts2组合模式的应用
暂时没发现,如果读者有发现,还请私信我添加,谢谢