薛磊 Job Seeker

23种设计模式——8.Composite组合(结构型模式)


对象容器的问题

​  在面向对象系统中,我们常会遇到一类具有“容器”特征的对象——即他们在充当对象的同时,又是其他对象的容器。

public class SingleBox extends IBox{
    public void process() {....}
}
public class ContainerBox extends IBox{
    public void process() {....}
    public ArrayList getBoxes(){....}
}
//如果我们要对这样的对象容器进行处理:
IBox box = Factory.getBox();
if(box instanceof ContainerBox){
    box.process();
    ArrayList list = ((ContrainerBox)box).getBoxes();//包含递归
}else if (box instanceof SingleBox){
    box.process();
}

动机(Motivation)

​  上述描述的问题根源在于:客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。

​  如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

意图(Intent)

​  将对象组合成树形结构以表示“部分-整体”的从层次结构,使得用户对单个对象和组合对象的使用具有一致性。

结构(Structure)

组合模式结构

  • Component 抽象构件角色

  • Leaf 叶子构件

  • Composite 树枝构件

Composite模式的几个要点

  • Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。

  • 将“客户代码与复杂的对象容器结构:解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复杂内部实现结构——发生依赖关系,从而更能”应对变化“。

  • Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性“”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.NET控件的实现在这方面为我们提供了一个很好的示范。

  • Composite模式是在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

示例代码(透明模式)

抽象构件:

public abstract class Component {
	//个体和整体都具有的共享
	public void doSomething() {}
	
	//增加一个叶子构件或树枝构件
	public abstract void add(Component component);

	//删除一个叶子构件或树枝构件
	public abstract void remove(Component component);

	//获得分支下的所有叶子构件和树枝构件
	public abstract ArrayList<Component) getChildren();
}

树叶节点:

public class Leaf extends Component {
	@Deprecared
	public void add(Component component) throws Exception{
		//空实现,直接抛出一个“不支持请求”的异常	
		throw new UnsupportedOperationException();
	}

	@Deprecared
	public void remove(Component component) throws Exception{
		//空实现
		throw new UnsupportedOperationException();
	}

	@Deprecared
	public ArrayList<Component> getChildren() throws Exception{
		//空实现	
		throw new UnsupportedOperationException();
	}
}

树枝构件:

public class Composite extends Component {
	//构件容器
	private ArrayList<Component> list = new ArrayList<Component>();

	//增加一个叶子构件或树枝构件
	public void add(Component component){
		this.list.add(component);
	}

	//删除一个叶子构件或树枝构件
	public abstract void remove(Component component){
		this.list.remove(component);
	}

	//获得分支下的所有叶子构件和树枝构件
	public abstract ArrayList<Component) getChildren(){
		return this.list;
	}
}

场景类:

public class Client {
	public static void main(String[] args) {
		//创建一个根节点
		Composite root = new Composite();
		root.doSomething();

		//创建一个树枝构件
		Composite branch = new Composite();
		
		//创建一个叶子节点
		Leaf leaf = new Leaf();

		//建立整体
		road.add(branch);
		branch.add(leaf);
	}

//通过递归遍历树
public staic void display(Component root) {
	for(Component c : root.getChildren()){
		if(c instanceof Leaf){
			c.doSomething();
		}else{
			//如果是安全模式,传进来的参数是Composite类型,遍历的时候转成了Component类型,所以需要在c前面进行强制转换(Composite)c
			display(c);
		}
	}
}

Comments

Content