JAVA设计模式(20) —组合(Composite)模式

定义:将对象组合成树形结构以表示 “部分 — 整体” 的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。

类型:对象结构型模式

类图:



组合模式的结构

  • 抽象构件角色(component):是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
  • 这个接口可  以用来管理所有的子对象。(可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。  
  • 树叶构件角色(Leaf):在组合树中表示叶节点对象,叶节点没有子节点。并在组合中定义图元对象的行为。
  • 树枝构件角色(Composite):定义有子部件的那些部件的行为。存储子部件。在Component接口中实现与子部件有关的操作。
  • 客户角色(Client):通过component接口操纵组合部件的对象。   


组合模式(Composite)有两种实现方式:透明方式和安全方式
透明方式:
在Component中声明所有用来管理子对象的方法,如Add()方法,Remove()方法及GetChild()方法,所有实现Component接口的子类都具备这些方法,这使得Component和子类具备一致的行为接口,使得对客户端无需区别树叶和树枝对象。
大家可以回忆一下代理模式(Proxy)中,Proxy,RealSubject类和Subject接口具备一致的行为接口,从而使得被代理者对于客户端是透明的。
正由于我们的Composite和Leaf都具备一致的接口行为,但我们知道Leaf不应该具有Add(),Remove()及GetChild()方法,因为我们叶子节点不能再添加和移除节点了。
安全模式:在透明模式基础上把Component中声明所有用来管理子对象的方法移到Composite中,在Composite实现子对象的管理方法,那么Leaf就没有子对象管理方法,这使得Composite和Leaf的行为接口不一致,所以客户端在调用时要知道树叶和树枝对象存在。


Composite模式透明方式实现代码

首先我们定义一个抽象类Componet,在其中声明add(),remove()等子对象操作

// 抽象类接口  Component
abstract class Component {
	public abstract int getChildNum();
	public abstract String getName();
	public abstract String getType();
	public abstract void add(Component c);
	public abstract void remove(Component c);
}


接着我们定义Composite类继承于Component,增加childList保存Component对象的引用,从而建立起由Component到Composite的聚集关系(Has-a关系),并且实现Component抽象类中的抽象方法。

// 树枝节点  Composite类继承于Component
class Composite extends Component {
	private String nodeName;
	private List<Component> childList;	//保存孩子节点

	public Composite(String nodeName) {
		this.nodeName = nodeName;
		childList = new ArrayList<Component>();
	}

	@Override
	public int getChildNum() {

		return childList.size();
	}

	@Override
	public String getName() {

		return this.nodeName;
	}

	@Override
	public String getType() {

		return "Composite";
	}

	@Override
	public void add(Component c) {
		childList.add(c);
	}

	@Override
	public void remove(Component c) {
		childList.remove(c);
	}
}

最后我们定义Leaf类它也是继承于Component,在其中我们实现Component定义的行为接口,这使得Composite和Leaf类具有统一的接口行为,但我们并没有在Leaf的行为方法中给出具体的实现,因为叶子节点并没有后继,所以没有必要实现Add()和Remove()等行为。

// 叶子节点 Leaf类 也继承于Component
class Leaf extends Component {
	private String nodeName;

	public Leaf(String nodeName) {
		this.nodeName = nodeName;
	}

	@Override
	public int getChildNum() {

		return 0;
	}

	@Override
	public String getName() {

		return this.nodeName;
	}

	@Override
	public String getType() {

		return "Leaf";
	}

	@Override
	public void add(Component c) {
		System.err.println("ERROR ! Leaf Not supported add method!");
	}

	@Override
	public void remove(Component c) {
		System.err.println("ERROR ! Leaf Not supported remove method!");
	}
}

客户调用

//客户 Client
public class CompositeClient {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Component root = new Composite("root");	//根节点

		Component composite = new Composite("composite");	//树枝节点

		//叶节点
		Component leaf1 = new Leaf("leaf1");
		Component leaf2 = new Leaf("leaf2");

		composite.add(leaf2);
		root.add(leaf1);
		root.add(composite);

		String str = "Leaf1's size is " + leaf1.getChildNum();
		str += "\nleaf2's size is " + leaf2.getChildNum();
		str += "\nComposite's size is " + composite.getChildNum();
		str += "\nRoot's size is " + root.getChildNum();
		
		System.out.println(str);

	}
}


Composite适用场景

  1. 想表示对象的 部分 — 整体 层次结构。
  2. 希望用户忽略 组合对象与单个对象的不同,用户将统一的使用组合结构中的所有对象。


Composite组合模式的几个要点:

  • Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致的处理对象和对象容器,无需关心处理的是单个对象,还是组合的对象容器。
  • 将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的对象接口——而非对象容器的复杂内部实现结构——发生依赖关系,从而更能“应对变化”。
  • Composite模式中,是将“Add和Remove的和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡结构,这又是必须付出的代价。
  • Composite模式在具体实现中,可以让父对象中的字对象反向追溯:如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率 


总结

  1. 组合模式(Composite)采用树形层次结构来实现对象容器,如:绘图程序例子我们把各种各样的图形添加的Shape中,从而构造起一条对象链,把“一对多”的关系转化“一对一”的层次关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
  2. 组合模式(Composite)中,透明方式和安全方式的使用抉择,虽然透明方式有可能违背面向对象的SRP原则(单一职责),而安全方式没有很好的封装变化,但在实际开发时我们要根据具体的情况权衡利弊。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值