定义与特点
有时叫做整体-部分模式,将对象组合成树状的层次结构的设计模式,用来表示整体-部分的关系,使得用户对单个对象和组合对象有一致的访问性。
将对象组织到树形结构中,
优点
- 组合模式使得客服端代码可以一致的处理单个对象和组合对象,无须关心自己处理的单个对象,可以直接进行操作,给客户带来了极大的便利
- 更容易在组合体内加入新的对象,客户端不会因为加入新的对象而更改源代码,满足开闭原则
缺点
- 设计比较复杂,客户端需要花更多的时间来理清类之间的层次关系
- 不容易限制容器中的构件
- 不容易继承的方法来增加构件的新功能
组合模式的应用场景
- 需要表示一个对象整体与部分的层次结构的场合
- 要求对用户隐藏组合对象与单个对象不同,用户可以用统一的接口使用组合结构中的所有对象的场合
代码结构
- 抽象构件(Component):主要作用是为树叶构件和树枝构件声明公共接口,并实现他们的默认行为。
- 在透明式的组合中:还声明访问和管理子类的接口
- 在安全式组合中,不声明访问和管理子类接口,管理工作由树枝构件完成。
- 树叶构件(Leaf):是组合中的叶子节点对象,没有子节点,用于继承或实现抽象构件
- 树枝构件(Composite)/中间构件: 组合中的分支系欸但对象,有子节点。用于继承和思实现抽下管理构件。主要作用是存储和管理子部件。通常包含了 Add(),Remove(),GetChild()等方法。
透明模式代码实现
抽象构件声明了所有的子类中所有的方法,所以客户端不需要区别对待树枝和树叶对象,对客户端来说是透明的。
但是,树叶构件本没有Add(),Remove()等方法,但是要去实现他们(为空或者抛异常),会带来一部分的安全问题
抽象构件
public interface ComponentLucency {
public void add(ComponentLucency c);
public void remove(ComponentLucency c);
public ComponentLucency getChild(int t);
public void operation();
}
树叶构件
public class LeafLucency implements ComponentLucency{
private String name;
public LeafLucency(String name){
this.name = name;
}
@Override
public void add(ComponentLucency c) {
}
@Override
public void remove(ComponentLucency c) {
}
@Override
public ComponentLucency getChild(int t) {
return null;
}
@Override
public void operation() {
System.out.println("树叶"+name+":被访问!!!");
}
}
树枝构件
public class CompositeLucency implements ComponentLucency{
private ArrayList<ComponentLucency> children = new ArrayList<>();
@Override
public void add(ComponentLucency c) {
children.add(c);
}
@Override
public void remove(ComponentLucency c) {
children.remove(c);
}
@Override
public ComponentLucency getChild(int t) {
return children.get(t);
}
@Override
public void operation() {
for (Object obj:children) {
((ComponentLucency)obj).operation();
}
}
}
调用
public class LucencyMain {
public static void main(String[] args) {
ComponentLucency c0 = new CompositeLucency();
ComponentLucency c1 = new CompositeLucency();
ComponentLucency l1 =new LeafLucency("1");
ComponentLucency l2 =new LeafLucency("2");
ComponentLucency l3 =new LeafLucency("3");
c0.add(l1);
c0.add(c1);
c1.add(l2);
c1.add(l3);
c0.operation();
System.out.println("-------------------------------");
c1.operation();
}
}
安全模式代码实现
将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子类对象的管理方法,这样避免了上一种的安全性问题,但是由于叶子和分支有不同的接口,客户端在调用时需要知道树叶对象和树枝对象的存在,失去了透明性。
示例:
* 组合模式的例子(安全模式下)
*
* 最后“大袋子”中的内容有:{
* 1 双李宁牌运动鞋(单价 198 元)、
* 白色小袋子{
* 2 包韶关香菇(单价 68 元)、
* 3 包韶关红茶(单价 180 元)}、
* 中袋子{
* 1 个景德镇瓷器(单价 380 元)、
* 红色小袋子{
* 2 包婺源特产(单价 7.9 元)、
* 1 张婺源地图(单价 9.9 元)}}},
* 现在要求编程显示李先生放在大袋子中的所有商品信息并计算要支付的总价。
抽象构件
public abstract class Atricles {
//计算价格方法
public abstract float calculation();
//显示商品信息方法
public abstract void show();
}
树叶构件
public class Goods extends Atricles {
private String name;
private int quantity;
private float unitPrie;
public Goods(String name,int quantity,float unitPrie){
this.name = name;
this.quantity = quantity;
this.unitPrie = unitPrie;
}
@Override
public float calculation() {
return quantity*unitPrie;
}
@Override
public void show() {
System.out.println("商品名:"+name +" 单价:" + unitPrie + " 数量: " + quantity + " 总价: "+ calculation());
}
}
树枝构件
public class Bags extends Atricles{
private String name;
private ArrayList<Atricles> list = new ArrayList<>();
public Bags(String name){
this.name = name;
}
public void add(Atricles a){
list.add(a);
}
public void remove(Atricles a){
list.remove(a);
}
public Atricles getChild(int i){
return list.get(i);
}
@Override
public float calculation() {
float price = (float) 0;
for (Object obj:list) {
price = price + ((Atricles)obj).calculation();
}
return price;
}
@Override
public void show() {
System.out.println("----------");
System.out.println(name + "装着下面的这些物品:");
for (Object obj:list) {
((Atricles)obj).show();
}
}
}
调用
public class CompositeSimpleMain {
public static void main(String[] args) {
Bags bags0 = new Bags("大袋子");
Bags bags1 = new Bags("白色小袋子");
Bags bags2 = new Bags("中袋子");
Bags bags3 = new Bags("红色小袋子");
Goods goods1 = new Goods("双李宁牌运动鞋",1,198);
Goods goods2 = new Goods("包韶关香菇",2,68);
Goods goods3 = new Goods("包韶关红茶",3,180);
Goods goods4 = new Goods("景德镇瓷器",1,380);
Goods goods5 = new Goods("婺源特产",2,7.9F);
Goods goods6 = new Goods("婺源地图",1,9.9F);
bags0.add(goods1);
bags0.add(bags1);
bags0.add(bags2);
bags1.add(goods2);
bags1.add(goods3);
bags2.add(goods4);
bags2.add(bags3);
bags3.add(goods5);
bags3.add(goods6);
bags0.show();
System.out.println("需要支付的价格:" +bags0.calculation());
}
}
结果
----------
大袋子装着下面的这些物品:
商品名:双李宁牌运动鞋 单价:198.0 数量: 1 总价: 198.0
----------
白色小袋子装着下面的这些物品:
商品名:包韶关香菇 单价:68.0 数量: 2 总价: 136.0
商品名:包韶关红茶 单价:180.0 数量: 3 总价: 540.0
----------
中袋子装着下面的这些物品:
商品名:景德镇瓷器 单价:380.0 数量: 1 总价: 380.0
----------
红色小袋子装着下面的这些物品:
商品名:婺源特产 单价:7.9 数量: 2 总价: 15.8
商品名:婺源地图 单价:9.9 数量: 1 总价: 9.9
需要支付的价格:1279.7