组合模式的定义&特点
组合(Composite Pattern)模式的定义:整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式,举个简单的例子,如下图所示:
电脑桌面为根节点,回收站、我的电脑、游戏文件夹为树叶节点,文件1、文件2、ppt文件、工作文件夹、王者荣耀为树叶节点。
先简单通过颜色进行区分,其实根节点和树枝节点本质上属于同一种数据类型,都为棕红色,都可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。组合模式中,会把树枝节点和叶子节点实现同一个接口,方便统一控制。
优点:
- 定义层次的关系上,又忽略了层次之间的差异,方便客户端对整个层次结构的控制。
- 新增、删除节点的时候,客户端无需修改代码,满足“开闭原则”;
缺点:
- 层级关系梳理复杂。
- 不容易限制类型,举个例子:游戏文件夹只能包含游戏类的文件,使用组合模式时,不能依赖类型系统进行约束,它们都来自于节点的抽象层,在这种情况下,必须通过在运行时进行类型检查,这样就变得比较复杂。
- 设计变得更加的抽象化。
组合模式的结构与实现
1. 组合模式的结构都有哪些角色组成?
答:组合模式包含以下3个主要角色。
(1)抽象构件(Component)角色 Interface:它作为树叶和树枝 共同实现的接口,定义共同的方法。
- 在透明式组合模式中 定义 了访问和管理树叶子类的方法;
- 在安全式的组合模式中 则 不定义 访问和管理树叶子类的方法,管理工作由树枝构件完成。 (2)树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
(3)树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件定义的方法。
2、组合模式有哪两种类型呢?
答:2种
(1) 透明方式
- 抽象构件接口声明了所有子类中的全部方法,客户端无须区别树叶对象和树枝对象。
- 树叶构件本来没有 Add()、Remove() 及 GetChild()方法,因为实现了抽象构建接口,被迫要实现它们,一般情况是抛出异常,或者空方法,这样会带来一些安全性问题。
为了更清晰的理解,我们举个栗子~上代码!
1、抽象构建类
/**
* 抽象构件类
**/
public abstract class Component {
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract Component getChild(int i);
public abstract ArrayList<Component> getChilds();
public abstract void touch();
public String name;
public Component(String name) {
this.name = name;
}
// 公有操作
public void getName() {
System.out.println(this.name);
}
}
2、树枝构件类
/**
* 树枝构件类
**/
public class Composite extends Component {
public Composite(String name) {
super(name);
this.children = new ArrayList<>();
}
private ArrayList<Component> children = new ArrayList<>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public ArrayList<Component> getChilds() {
return children;
}
public void touch() {
for (Object obj : children) {
((Component) obj).touch();
}
}
}
3、叶子构建类
/**
* 叶子构件
**/
public class Leaf extends Component {
Leaf(String name) {
super(name);
}
public void add(Component c) {
throw new UnsupportedOperationException("操作不支持");
}
public void remove(Component c) {
throw new UnsupportedOperationException("操作不支持");
}
public Component getChild(int i) {
throw new UnsupportedOperationException("操作不支持");
}
@Override
public ArrayList<Component> getChilds() {
throw new UnsupportedOperationException("操作不支持");
}
public void touch() {
System.out.println("执行:" + name + " 业务逻辑!");
}
}
3、测试
/**
* 测试
**/
public class CompositePattern {
public static void main(String[] args) {
Composite root = new Composite("树根节点");
Composite c0 = new Composite("树枝节点A");
Composite c1 = new Composite("树枝节点B");
Component leaf1 = new Leaf("树叶1");
Component leaf2 = new Leaf("树叶2");
Component leaf3 = new Leaf("树叶3");
Component leaf4 = new Leaf("树叶4");
root.add(c0);
root.add(c1);
c0.add(leaf1);
c1.add(leaf2);
c1.add(leaf3);
c1.add(leaf4);
root.touch();
recurrenceTree(root);
}
private static void recurrenceTree(Composite root) {
ArrayList<Component> children = root.getChilds();
for (Component c : children) {
if (c instanceof Leaf) {
System.out.print("\t");
c.getName();
} else {
c.getName();
// 递归
recurrenceTree((Composite) c);
}
}
}
}
执行结果如下:
(2) 安全方式
- 只有树枝构件中实现了管理子构件的方法,抽象构件不再声明对子对象的管理方法、树叶构件不再实现管理子类的方方,避免了透明方式中的安全性的问题。
- 测试类在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
为了便于理解,我们在透明模式的基础上升级为安全模式,大家一比对就清楚了。
1、抽象构建类-去掉了管理子类的方法
/**
* 抽象构件类、节点类
**/
public abstract class Component {
public abstract void touch();
public String name;
public Component(String name) {
this.name = name;
}
// 公有操作
public void getName() {
System.out.println(this.name);
}
}
2、叶子构建类-不再实现管理子类的方法。
/**
* 叶子构件
**/
public class Leaf extends Component {
Leaf(String name) {
super(name);
}
public void touch() {
System.out.println("执行:" + name + " 业务逻辑!");
}
}
说了半天,组合模式的实际应用场景有没有例子呢??
答:有 比如:计算公司预算情况,需要按照每个部门来进行分类、汇总,一个大公司下面有产品部门、研发部门等,产品部门可能分为一级、二级、三级等。
和之前的demo不太一样的地方,这里并没有定义为abstract,而是interface接口(原理一样~)上代码~ 1、抽象构建类
/**
* 抽象构建
**/
public interface Jiesuan {
//计算公司预算
float calculMoney();
//获取部门人员具体预算
void getMsg();
}
2、树枝构建类
/**
* 树枝构建
**/
public class JiesuanComposite implements Jiesuan {
private String name;
private ArrayList<Jiesuan> orderJiesuan = new ArrayList<>();
public JiesuanComposite() {
}
public JiesuanComposite(String name) {
this.name = name;
}
public void add(Jiesuan c) {
orderJiesuan.add(c);
}
public void remove(Jiesuan c) {
orderJiesuan.remove(c);
}
public Jiesuan getChild(int i) {
return orderJiesuan.get(i);
}
public float calculMoney() {
float s = 0;
for (Object obj : orderJiesuan) {
s += ((Jiesuan) obj).calculMoney();
}
return s;
}
@Override
public void getMsg() {
System.out.println(name+"预算");
for (Object obj : orderJiesuan) {
((Jiesuan) obj).getMsg();
}
}
}
3、叶子构建类
/**
*叶子构建
**/
public class Yusuan implements Jiesuan {
private String name; //名字
private Float peoplePrice; //预算
//默认构造器
public Yusuan() {
}
//自定义构造器
public Yusuan(String name, Float peoplePrice) {
this.name = name;
this.peoplePrice = peoplePrice;
}
@Override
public float calculMoney() {
return peoplePrice;
}
@Override
public void getMsg() {
System.out.println(name+":"+peoplePrice);
}
}
4、测试
/**
* 测试
**/
public class JiesuanPattern {
public static void main(String[] args) {
Float s ;
Yusuan yusuan;
JiesuanComposite gongsi = new JiesuanComposite("公司");
JiesuanComposite cpLevel1 =new JiesuanComposite("产品一级部门");
JiesuanComposite yfLevel1 = new JiesuanComposite("研发一级部门");
JiesuanComposite yfLevel2A = new JiesuanComposite("研发二级部门A");
JiesuanComposite yfLevel2B = new JiesuanComposite("研发二级部门B");
yusuan = new Yusuan("产品一级部门-孙三", 1117f);
cpLevel1.add(yusuan);
yusuan = new Yusuan("研发一级部门-孙四", 2222f);
yfLevel1.add(yusuan);
yusuan = new Yusuan("研发二级部门A-李莉", 1211f);
yfLevel2A.add(yusuan);
yusuan = new Yusuan("研发二级部门A-李磊", 1211f);
yfLevel2A.add(yusuan);
yusuan = new Yusuan("研发二级部门B-陈好", 2231f);
yfLevel2B.add(yusuan);
yfLevel1.add(yfLevel2A);
yfLevel1.add(yfLevel2B);
gongsi.add(cpLevel1);
gongsi.add(yfLevel1);
gongsi.getMsg();
s = gongsi.calculMoney();
System.out.println("公司总预算:" + s + "元");
}
}
结果打印如下: