天天看点

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

一、概述

我们常常可以看到这样一种形式,比如说电脑中的磁盘管理,我的电脑中有C盘、D盘、E盘,在C盘中又有A文件夹、B文件夹等,在A文件夹下又有A1文件夹、A2文件夹、A3文件等等;再比如说在桌子上有背包、电脑、水杯、书架、笔筒等,在背包里有书、本子、IPad等,在书架上有书A、书B等,在笔筒里有中性笔、钢笔、铅笔、彩笔等;再比如北京公司总部有财务部、人力部、上海分公司、深圳分公司等,在上海分公司又有财务部、人力部、南京办事处、杭州办事处等,在南京办事处下面又有财务部、人力部等。我这么说,可能看不出什么,下面用图表示一下这3个例子。

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

图1 我的电脑结构示意图

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

图2 桌面结构示意图

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

图3 公司结构示意图 

这样一看他们结构是不是一目了然,其实就是树形结构,有根、有枝干、有叶子。如果这个时候boss抛来一个需求,让所有的枝干具有一定的功能A,所有的叶子具有一定的功能B,或者,更具体点,从图3来说,要求所有公司的财务部具有相同的功能A,人力部具有功能B,这个时候我们要怎么实现呢。

当当当当~ 组合模式应运而生!

二、组合模式

1. 定义

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。(引自《大话设计模式》)

2. 基本结构图

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

Component:抽象类或接口,为组合中的对象声明接口。通过add和remove方法提供增加和删除树枝、树叶的功能。

Leaf:叶子,为组合中最末端的节点,没有子节点。

Composite:枝节点,上有父节点,下有子节点。定义了枝节点的行为,用来存储子部件。在Component接口中实现与子部件有关的操作——增加add和删除remove。

3. 基本代码

Component抽象类

public abstract class Component {
	protected String name;
	
	public Component(String name) {
		this.name = name;
	}
	
	public abstract void add(Component c);
	public abstract void remove(Component c);
	public abstract void display(int depth);
}
           

Leaf叶子类,由于叶子没有子节点,无需再增叶子和树枝,所以这里的add和remove方法就没有实现。但却保留了这两个方法,是为了与树枝节点Composite类保持一致的接口,以消除叶节点和枝节点在抽象层次的区别。

public class Leaf extends Component{
	 
	public Leaf(String name) {
		super(name);
	}
	
	@Override
	public void add(Component c) {
		
	}

	@Override
	public void remove(Component c) {
		
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

}
           

Composite枝节点类

public class Composite extends Component{
	private List<Component> children = new ArrayList<Component>();
	
	public Composite(String name) {
		super(name);
	}
	
	@Override
	public void add(Component c) {
		children.add(c);
	}

	@Override
	public void remove(Component c) {
		if(children.contains(c)) {
			children.remove(c);
		}
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
		for (Component component : children) {
			component.display(depth+2);
		}
	}

}
           

测试类

public class CompositeTest {

	public static void main(String[] args) {
		//创建根节点
		Composite root = new Composite("Root");
		//添加两个叶节点
		root.add(new Leaf("Leaf A"));
		root.add(new Leaf("Leaf B"));
		
		//创建枝节点
		Composite c1 = new Composite("Composite X");
		//给枝节点X添加两个叶节点
		c1.add(new Leaf("Leaf XA"));
		c1.add(new Leaf("Leaf XB"));
		
		//将枝节点X添加到根节点上
		root.add(c1);
		
		//创建枝节点
		Composite c2 = new Composite("Composite XY");
		//给枝节点XY添加两个叶节点
		c2.add(new Leaf("Leaf XYA"));
		c2.add(new Leaf("Leaf XYB"));
		
		//将枝节点XY添加到枝节点X上
		c1.add(c2);
		
		//给根节点添加叶节点C
		root.add(new Leaf("Leaf C"));
		
		//创建叶节点D
		Leaf d = new Leaf("Leaf D");
		//将叶节点D添加到根节点上
		root.add(d);
		//将叶节点D从根节点移除
		root.remove(d);
		
		//显示树状结构结果
		root.display(1);
	}

}
           

结果:

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

用一张图来表示这个结果的话,如下

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

进一步说明,这个例子里的display方法只是为了输出结果显示出比较明确的层次结构,实际应用中可以根据需求做相应调整。

三、透明方式与安全方式

1. 透明方式

 上面例子里,叶节点Leaf类里面的add和remove方法其实是无意义的方法,没有实现内容,这种方式叫做透明方式。

即,在Component接口中声明所有用来管理子对象的方法,包括add和remove等,这样实现Component接口的所有子类都具备了add和remove方法。

这样做的优点,上面也提到过了,就是让枝节点和叶节点对外界来说没有区别,具备完全一致的行为接口。

相应的缺点就是,叶节点实现了完全没有任何意义的add和remove方法。

2. 安全方式

与透明方式相对应,如果叶节点Leaf类中不用add和remove方法,这种方式就叫做安全方式。

即,在Component接口中不声明add和remove方法,而是在子部件Composite中声明所有用来管理子类对象的方法。

这样做的优点就是上面提到的透明方式的缺点,这里不会在Leaf类中出现无意义的方法。

相应的缺点也很明显,就是在客户端调用时需要做判断进行区分,稍稍麻烦一些。

四、实战应用

这里就用上面的公司结构来举例说明好了

抽象公司类Company,就是上面的Component

public abstract class Company {
	protected String name;
	
	public Company(String name) {
		this.name = name;
	}
	
	public abstract void add(Company company);
	public abstract void remove(Company company);
	public abstract void display(int depth);
	public abstract void duty();
}
           

具体公司实现类ConcreteCompany,就是上面的Composite

public class ConcreteCompany extends Company{
	//声明公司集合
	private List<Company> companies = new ArrayList<Company>();
	
	public ConcreteCompany(String name) {
		super(name);
	}
	
	/**
	 * 添加子公司
	 */
	@Override
	public void add(Company company) {
		companies.add(company);
	}

	/**
	 * 删除子公司
	 */
	@Override
	public void remove(Company company) {
		if (companies.contains(company)) {
			companies.remove(company);
		}
	}

	/**
	 * 显示
	 */
	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
		for (Company company : companies) {
			company.display(depth+2);
		}		
	}

	/**
	 * 公司职责
	 */
	@Override
	public void duty() {
		for (Company company : companies) {
			company.duty();
		}		
	}

}
           

人力部HRCompany,就是上面的叶子类Leaf

/**
 * 人力资源部
 */
public class HRDepartment extends Company{

	public HRDepartment(String name) {
		super(name);
	}

	@Override
	public void add(Company company) {
		
	}

	@Override
	public void remove(Company company) {
		
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

	@Override
	public void duty() {
		System.out.println(name+"职责:招聘人才");
	}

}
           

财务部FinanceCompany,就是上面的Leaf。这个例子中有两个Leaf——人力部和财务部,它们都没有子节点,符合叶子特征,但是由于这里人力部和财务部具有不同的公司职责,所以分开两个实现。

/**
 * 财务部
 */
public class FinanceDepartment extends Company{

	public FinanceDepartment(String name) {
		super(name);
	}

	@Override
	public void add(Company company) {
		
	}

	@Override
	public void remove(Company company) {
		
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

	@Override
	public void duty() {
		System.out.println(name+"职责:公司财务管理");
	}

}
           

测试类

public class CompositeTest {

	public static void main(String[] args) {
		ConcreteCompany root = new ConcreteCompany("北京总公司");
		
		root.add(new HRDepartment("总公司人力部"));
		root.add(new FinanceDepartment("总公司财务部"));
		
		ConcreteCompany c1 = new ConcreteCompany("上海分公司");
		c1.add(new HRDepartment("上海分公司人力部"));
		c1.add(new FinanceDepartment("上海分公司财务部"));
		
		root.add(c1);
		
		ConcreteCompany c2 = new ConcreteCompany("南京办事处");
		c2.add(new HRDepartment("南京办事处人力部"));
		c2.add(new FinanceDepartment("南京办事处财务部"));
		
		c1.add(c2);
		
		ConcreteCompany c3 = new ConcreteCompany("杭州办事处");
		c3.add(new HRDepartment("杭州办事处人力部"));
		c3.add(new FinanceDepartment("杭州办事处财务部"));
		
		c1.add(c3);
		
		System.out.println("各公司名称:");
		root.display(1);
		
		System.out.println("\n各部门职责:");
		root.duty();
	}

}
           

结果

Java设计模式之组合模式(Composite)一、概述二、组合模式三、透明方式与安全方式四、实战应用五、总结

五、总结

使用场合:

(1)如果各对象体现出“部分-整体”层次结构(或树形结构)时,可以考虑使用组合模式。

(2)当客户希望忽略单个对象与组合对象的区别,可以使用统一接口调用时,可以考虑使用组合模式。

组合模式,简单易用,核心其实就是一个递归和组合,展开来说就是一个很复杂的对象由许多基本对象构成,其中基本对象又可以构成稍复杂一些的组合对象,而组合对象又可以被重组成更复杂的组合对象,如此递归下去就构成了最终的对象。

写在最后,

本文主要是小猫看了《大话设计模式》的一些记录笔记,再加之自己的一些理解整理出此文,方便以后查阅,仅供参考。