设计模式16---设计模式之组合模式(Composite)(行为型)

1.场景模拟

使用软件模拟大树的根节点和树枝节点和叶子节点
抽象为两类,容器节点和叶子节点

2.不用模式的解决方案

package demo14.composite.example1;
import java.util.*;


/**
 * 组合对象,可以包含其它组合对象或者叶子对象
 */
public class Composite {
	/**
	 * 用来记录包含的其它组合对象
	 */
	private Collection<Composite> childComposite = new ArrayList<Composite>();
	/**
	 * 用来记录包含的其它叶子对象
	 */
	private Collection<Leaf> childLeaf = new ArrayList<Leaf>();
	/**
	 * 组合对象的名字
	 */
	private String name = "";
	/**
	 * 构造方法,传入组合对象的名字
	 * @param name 组合对象的名字
	 */
	public Composite(String name){
		this.name = name;
	}
	/**
	 * 向组合对象加入被它包含的其它组合对象
	 * @param c 被它包含的其它组合对象
	 */
	public void addComposite(Composite c){
		this.childComposite.add(c);
	}
	/**
	 * 向组合对象加入被它包含的叶子对象
	 * @param leaf 被它包含的叶子对象
	 */
	public void addLeaf(Leaf leaf){
		this.childLeaf.add(leaf);
	}
	/**
	 * 输出组合对象自身的结构
	 * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
	 */
	public void printStruct(String preStr){
		//先把自己输出去
		System.out.println(preStr+"+"+this.name);
		//然后添加一个空格,表示向后缩进一个空格,输出自己包含的叶子对象
		preStr+=" ";
		for(Leaf leaf : childLeaf){
			leaf.printStruct(preStr);
		}
		//输出当前对象的子对象了
		for(Composite c : childComposite){
			//递归输出每个子对象
			c.printStruct(preStr);
		}
	}
}
***********************************************************************************************
package demo14.composite.example1;
/**
 * 叶子对象
 */
public class Leaf {
	/**
	 * 叶子对象的名字
	 */
	private String name = "";
	/**
	 * 构造方法,传入叶子对象的名字
	 * @param name 叶子对象的名字
	 */
	public Leaf(String name){
		this.name = name;
	}
	/**
	 * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
	 * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
	 */
	public void printStruct(String preStr){
		System.out.println(preStr+"-"+name);
	}
}
***************************************************************************************************
package demo14.composite.example1;


public class Client {
	public static void main(String[] args) {
		// 定义所有的组合对象
		Composite root = new Composite("服装");
		Composite c1 = new Composite("男装");
		Composite c2 = new Composite("女装");
		// 定义所有的叶子对象
		Leaf leaf1 = new Leaf("衬衣");
		Leaf leaf2 = new Leaf("夹克");
		Leaf leaf3 = new Leaf("裙子");
		Leaf leaf4 = new Leaf("套装");
		// 按照树的结构来组合组合对象和叶子对象
		root.addComposite(c1);
		root.addComposite(c2);


		c1.addLeaf(leaf1);
		c1.addLeaf(leaf2);


		c2.addLeaf(leaf3);
		c2.addLeaf(leaf4);


		// 调用根对象的输出功能来输出整棵树
		root.printStruct("");
	}
}

3.问题所在

大家很容易就发现了一个问题:那就是必须区分叶子节点和容器节点,无论是客户端还是内部都要区分,那么这样就会使得程序变得复杂,对于功能的扩展也不方便,而且要知道两个类才行,那么我们能不能把他整合成一个类呢,让客户端如下显示呢?答案是可以的。

package demo14.composite.example2;


public class Client {
	public static void main(String[] args) {
		//定义多个Composite对象
		Component root = new Composite();
		Component c1 = new Composite();
		Component c2 = new Composite();
		//定义多个叶子对象
		Component leaf1 = new Leaf();
		Component leaf2 = new Leaf();
		Component leaf3 = new Leaf();
		
		//组和成为树形的对象结构
		root.addChild(c1);
		root.addChild(c2);
		root.addChild(leaf1);
		
		c1.addChild(leaf2);
		c2.addChild(leaf3);
		
		//操作Component对象
		Component o = root.getChildren(1);
		System.out.println(o);
	}
}

4.解决方案的示例代码

很明显,上述客户端的代码就顺眼多了,那么我们改如何实现呢?

4.1首先看结构图

  设计模式16---设计模式之组合模式(Composite)(行为型)_第1张图片

4.2组合模式定义

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

4.3示例代码如下

package demo14.composite.example2;


/**
 * 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
 */
public abstract class Component {
	/**
	 * 示意方法,子组件对象可能有的功能方法
	 */
	public abstract void someOperation();


	/**
	 * 向组合对象中加入组件对象
	 * 
	 * @param child
	 *        被加入组合对象中的组件对象
	 */
	public void addChild(Component child) {
		// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
		throw new UnsupportedOperationException("对象不支持这个功能");
	}


	/**
	 * 从组合对象中移出某个组件对象
	 * 
	 * @param child
	 *        被移出的组件对象
	 */
	public void removeChild(Component child) {
		// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
		throw new UnsupportedOperationException("对象不支持这个功能");
	}


	/**
	 * 返回某个索引对应的组件对象
	 * 
	 * @param index
	 *        需要获取的组件对象的索引,索引从0开始
	 * @return 索引对应的组件对象
	 */
	public Component getChildren(int index) {
		// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
		throw new UnsupportedOperationException("对象不支持这个功能");
	}
}
**********************************************************************
package demo14.composite.example2;


import java.util.*;


/**
 * 组合对象,通常需要存储子对象,定义有子部件的部件行为,
 * 并实现在Component里面定义的与子部件有关的操作
 */
public class Composite extends Component {
	/**
	 * 用来存储组合对象中包含的子组件对象
	 */
	private List<Component> childComponents = null;


	/**
	 * 示意方法,通常在里面需要实现递归的调用
	 */
	public void someOperation() {		
		if (childComponents != null){
			for(Component c : childComponents){
				//递归的进行子组件相应方法的调用
				c.someOperation();
			}
		}
	}
	public void addChild(Component child) {
		//延迟初始化
		if (childComponents == null) {
			childComponents = new ArrayList<Component>();
		}
		childComponents.add(child);
	}


	public void removeChild(Component child) {
		if (childComponents != null) {
			childComponents.remove(child);
		}
	}


	public Component getChildren(int index) {
		if (childComponents != null){
			if(index>=0 && index<childComponents.size()){
				return childComponents.get(index);
			}
		}
		return null;
	}
}
**********************************************************************
package demo14.composite.example2;


/**
 * 叶子对象,叶子对象不再包含其它子对象
 */
public class Leaf extends Component {
	/**
	 * 示意方法,叶子对象可能有自己的功能方法
	 */
	public void someOperation() {
		// do something
	}
}
**********************************************************************
package demo14.composite.example2;


public class Client {
	public static void main(String[] args) {
		//定义多个Composite对象
		Component root = new Composite();
		Component c1 = new Composite();
		Component c2 = new Composite();
		//定义多个叶子对象
		Component leaf1 = new Leaf();
		Component leaf2 = new Leaf();
		Component leaf3 = new Leaf();
		
		//组和成为树形的对象结构
		root.addChild(c1);
		root.addChild(c2);
		root.addChild(leaf1);
		
		c1.addChild(leaf2);
		c2.addChild(leaf3);
		
		//操作Component对象
		Component o = root.getChildren(1);
		System.out.println(o);
	}
}

5.组合模式解决场景模拟

代码的修改量并不大,跟示例代码差不多

5.1结构图如下

  设计模式16---设计模式之组合模式(Composite)(行为型)_第2张图片

5.2代码如下(基本上同示例代码差不多,就不过多介绍了,注释非常清楚)

 

package demo14.composite.example3;


/**
 * 抽象的组件对象
 */
public abstract class Component {
	/**
	 * 输出组件自身的名称
	 */
	public abstract void printStruct(String preStr);


	/**
	 * 向组合对象中加入组件对象
	 * 
	 * @param child
	 *        被加入组合对象中的组件对象
	 */
	public void addChild(Component child) {
		// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
		throw new UnsupportedOperationException("对象不支持这个功能");
	}


	/**
	 * 从组合对象中移出某个组件对象
	 * 
	 * @param child
	 *        被移出的组件对象
	 */
	public void removeChild(Component child) {
		// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
		throw new UnsupportedOperationException("对象不支持这个功能");
	}


	/**
	 * 返回某个索引对应的组件对象
	 * 
	 * @param index
	 *        需要获取的组件对象的索引,索引从0开始
	 * @return 索引对应的组件对象
	 */
	public Component getChildren(int index) {
		// 缺省的实现,抛出例外,因为叶子对象没有这个功能,或者子组件没有实现这个功能
		throw new UnsupportedOperationException("对象不支持这个功能");
	}
}
*********************************************************************************************
package demo14.composite.example3;
import java.util.*;


/**
 * 组合对象,可以包含其它组合对象或者叶子对象
 */
public class Composite extends Component{
	/**
	 * 用来存储组合对象中包含的子组件对象
	 */
	private List<Component> childComponents = null;
	/**
	 * 组合对象的名字
	 */
	private String name = "";
	/**
	 * 构造方法,传入组合对象的名字
	 * @param name 组合对象的名字
	 */
	public Composite(String name){
		this.name = name;
	}
	
	public void addChild(Component child) {
		//延迟初始化
		if (childComponents == null) {
			childComponents = new ArrayList<Component>();
		}
		childComponents.add(child);
	}
	/**
	 * 输出组合对象自身的结构
	 * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
	 */
	public void printStruct(String preStr){
		//先把自己输出去
		System.out.println(preStr+"+"+this.name);
		//如果还包含有子组件,那么就输出这些子组件对象
		if(this.childComponents!=null){
			//然后添加一个空格,表示向后缩进一个空格
			preStr+=" ";		
			//输出当前对象的子对象了
			for(Component c : childComponents){
				//递归输出每个子对象
				c.printStruct(preStr);
			}
		}
	}
}
**********************************************************************************************
package demo14.composite.example3;


/**
 * 叶子对象
 */
public class Leaf extends Component {
	/**
	 * 叶子对象的名字
	 */
	private String name = "";


	/**
	 * 构造方法,传入叶子对象的名字
	 * 
	 * @param name
	 *        叶子对象的名字
	 */
	public Leaf(String name) {
		this.name = name;
	}


	/**
	 * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
	 * 
	 * @param preStr
	 *        前缀,主要是按照层级拼接的空格,实现向后缩进
	 */
	public void printStruct(String preStr) {
		System.out.println(preStr + "-" + name);
	}
}
********************************************************************************************
package demo14.composite.example3;


public class Client {
	public static void main(String[] args) {
		//定义所有的组合对象
		Component root = new Composite("服装");
		Component c1 = new Composite("男装");
		Component c2 = new Composite("女装");
		//定义所有的叶子对象
		Component leaf1 = new Leaf("衬衣");
		Component leaf2 = new Leaf("夹克");
		Component leaf3 = new Leaf("裙子");
		Component leaf4 = new Leaf("套装");
		//按照树的结构来组合组合对象和叶子对象
		root.addChild(c1);
		root.addChild(c2);
		
		c1.addChild(leaf1);
		c1.addChild(leaf2);
		
		c2.addChild(leaf3);
		c2.addChild(leaf4);
		
		//调用根对象的输出功能来输出整棵树
		root.printStruct("");
	}
}

6.组合模式讲解

6.1要点

目的:客户端不再区分叶子节点和组合对象
关键点:设计一个抽象的组合对象

6.2组合模式本质

统一叶子对象和组合对象

你可能感兴趣的:(设计模式)