组合模式

实例场景:咱们平时操做文件夹的复制,不管是文件中包含文件或者文件中的子文件咱们都会一并将其复制到相应的目录下面,在这个场景中咱们使用了组合模式。java

组合模式:属于结构性模式,它描述了对象间的组合关系。对象间经常经过树结构来组织(包含)起来,以实现总体-部分的层次结构。总体上能够看作是一个组合对象。抛却各类复杂的术语。缓存

组合模式的特色是对象经过实现(继承)统一的接口(抽象类),调用者对单一对象和组合对象的操做具备一致性。安全

场景分析:文件夹系统就是一个树形结构,它里面包含了总体与部分的层次结构,文件和文件夹提供给用户的操做是同样的,好比说复制文件或文件夹,因而,能够经过一个统一的接口将文件和文件夹统一块儿来,对用户提供一致的操做,屏蔽不一样的复制实现过程。咱们在复制文件夹的时候,操做系统实现了对文件夹内的全部文件和文件夹的复制,即实现了组合对象的总体复制,而不是一个空的文件夹;这和咱们复制单个文件的体验是一致的。这即是组合模式的妙处。ide

组合模式的角色测试

抽象构件角色(Component):为组合的对象声明接口,在某些状况下实现今后接口派生出的全部类共有的默认行为;定义一个接口能够访问及管理它的多个子部件ui

叶部件(Leaf):在组合中表示叶节点对象,叶节点没有子节点;定义组合中接口对象的行为this

组合类(Composite):定义有子节点(子部件)的部件的行为;存储子节点;在Component接口中实现与子部件相关的操做。spa

客户端(Client):经过Component接口控制组合部件的对象。操作系统

场景代码实现:code

package cn.com.composite; //抽象构件角色,为全部文件及文件夹定义统一的接口 public interface Component { 	public void copy();//定义一个文件复制的方法 } 
package cn.com.composite; //文件,至关与叶子节点(叶部件) public class MyFile implements Component { 	private String name; 	public MyFile(String name){ 		this.name=name; 	} 	@Override 	public void copy() { 		System.out.println("复制文件:"+name); 	} } 
package cn.com.composite;  import java.util.ArrayList; import java.util.List;  //文件夹,至关与组合类(Composite) public class Folder implements Component { 	private String name; 	public Folder(String name){ 		this.name=name; 	} 	 	//里面装Component表示能够操做Component 	List<Component> list=new ArrayList<Component>(); 	//增长构件角色,增长文件或者文件夹 	public void add(Component component){ 		list.add(component); 	} 	//删除文件或文件夹 	public void remove(Component componet){ 		list.remove(componet); 	} 	//返回全部文件及文件夹 	public List<Component> getAll(){ 		return this.list; 	} 	//对文件及文件夹进行复制操做 	@Override 	public void copy() { 		System.out.println("复制文件夹:"+name); 		//遍历文件及文件夹,若是是文件就会复制,若是是文件夹 		//就会遍历文件夹中的文件进行复制,隐藏了一个递归操做 		for(Component component:list){ 			component.copy(); 		} 	} } 
package cn.com.composite;  public class Client { 	public static void main(String[] args) { 		Component file1=new MyFile("你是个人眼.mp3"); 		Component file2=new MyFile("伤不起.mp3"); 		Folder music=new Folder("个人音乐"); 		music.add(file1); 		music.add(file2); 		Component file3=new MyFile("个人资料.doc"); 		Component file4=new MyFile("个人图片.jpg"); 		Folder folder=new Folder("个人文件"); 		folder.add(file3); 		folder.add(file4); 		folder.add(music);//添加一个文件夹 		folder.copy(); 		 		 		 		 	}  } 

结果输出:复制文件夹:个人文件    复制文件:个人资料.doc   复制文件:个人图片.jpg  复制文件夹:个人音乐  复制文件:你是个人眼.mp3   复制文件:伤不起.mp3

由以上的代码和运行结果可知:

经过实现组合模式,用户对文件夹的操做与对普通文件的操做并没有差别。用户彻底不用关心这是文件夹仍是文件,也不用关心文件夹内部的具体结构,就能够完成相关操做。一样的道理,咱们能够表达以下:

经过实现组合模式,调用者对组合对象的操做与对单一对象的操做具备一致性。调用者不用关心这是组合对象仍是文件,也不用关心组合对象内部的具体结构,就能够调用相关方法,实现功能

类比JUnit中,当咱们定义TestCase时与suite,运行的时候发现不管运行多个测试案例仍是单个测试案例,他们运行没有什么差别,由于在这个里面就用到了咱们的组合模式,在Suite中遍历TestCase,执行其方法

仔细分析copy()方法的代码,咱们会发现,若是从面向过程的角度思考,组合模式经过递归原理实现了树结构(组合对象)的深度优先遍历

代码中还有一种实现方式:就是把那些方法都定义在Component接口中,在Leaf中对其进行空实现,那么咱们在Client端就均可以换成是Component引用了。

代码以下:

package cn.com.composite;  import java.util.List;  //抽象构件角色,为全部文件及文件夹定义统一的接口 public interface Component { 	public void copy();//定义一个文件复制的方法 	public void add(Component component); 	public void remove(Component componet); 	public List<Component> getAll(); } 

package cn.com.composite;  import java.util.List;  //文件,至关与叶子节点(叶部件) public class MyFile implements Component { 	private String name; 	public MyFile(String name){ 		this.name=name; 	} 	@Override 	public void copy() { 		System.out.println("复制文件:"+name); 	} 	@Override 	public void add(Component component) { 		 	} 	@Override 	public List<Component> getAll() { 		return null; 	} 	@Override 	public void remove(Component componet) { 	} } 
//Client均可以换成Component引用

               Component file1=new MyFile("你是个人眼.mp3"); 		Component file2=new MyFile("伤不起.mp3"); 		Component music=new Folder("个人音乐"); 		music.add(file1); 		music.add(file2); 		Component file3=new MyFile("个人资料.doc"); 		Component file4=new MyFile("个人图片.jpg"); 		Component folder=new Folder("个人文件"); 		folder.add(file3); 		folder.add(file4); 		folder.add(music);//添加一个文件夹 		folder.copy()

使用组合模式时考虑的几个问题

  1. Composite模式采用树形结构来实现广泛存在的对象容器,从而将“一对多”的关系转换为“一对一”的关系,使得客户代码能够一致地处理对象和对象容器,无需关系处理的是单个对象仍是组合的对象容器。
  2. 将客户代码与复杂的对象容器解耦是合成模式的核心思想,解耦以后,客户代码将与纯粹的抽象接口—----非对象容器的内部实现结构发生依赖关系。
  3. 有时候系统须要遍历一个树枝结构的子构件不少次,这时候能够考虑把遍历子构件的结构暂时存储在父构件里面做为缓存。
  4. Composite模式中,是将Add和Remove等和对象容器相关的方法定义在“表示抽象的Componont类”中,仍是定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,须要仔细权衡。
相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息