设计模式 —— 组合模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/starter_____/article/details/89259203

组合模式的定义

将对象组合成树形结构以表示 “ 部分-整体 ” 的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。该模式分为以下两种形式

安全形式的组合模式:
在这里插入图片描述

透明形式的组合模式:
在这里插入图片描述

其中,组合模式所包含的各组成部分的说明如下。

  1. Component:为组合模式中的对象声明接口
  2. Leaf:在组合模式中表示叶结点对象,叶结点对象没有子结点,实现所有在Component的操作(如上图的 operation())。
  3. Composite:表示组合部件(由自身和子节点组合而成),实现操纵子节点的所有方法(如上图的 add(),remove(),getChild()方法);实现所有在Component的操作(如上图的 operation())。
  4. Client:通过Component接口操纵组合部件的对象。

优点:

  • 定义了包含基本对象和组合对象的类层次结构,基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合。
  • 简化了客户代码。客户可以一致地使用组合结构和单个对象,通常用户不知道处理的是一个叶结点还是一个组合组件。
  • 使得更容易增加新类型的组件。新定义的Composite或Leaf子类自动与已有的结构和客户代码一起工作,客户程序不需要因为新的Component类而改变。
  • 使设计变得更通用。

缺点:

  • 在使用安全形式的组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

使用场景:

  • 您想表示对象的部分-整体层次结构(树形结构)
  • 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

场景引入

假设我们要创建一个树形菜单,菜单中的树枝和叶子具有相同的 operation() 方法,那么我们要如何设计程序,以便于我们遍历整个树形菜单呢?


方案一:安全形式的组合模式

在 Component 中不去声明管理子类对象的方法(如 add,remove,getChild),那么子类的 Leaf 就不需要实现它,而是在 Composit 声明所有用来管理子类对象的方法。但是安全模式与依赖倒置原则冲突,并且在遍历树形结构的的时候需要进行强制类型转换(如下文客户端代码中的 showTree((Composite)c) )。
在这里插入图片描述
步骤一:定义抽象控件

public abstract class Component {
	//个体和整体都具有
	public void operation(){
		//编写业务逻辑
	}
}

步骤二:定义 Leaf 控件

public class Leaf extends Component {
	/*
	* 可以覆写父类方法
	* public void operation(){
	*
	* }
	*/
}

步骤三:定义 Composite 控件

public class Composite extends Component {
	//构件容器
	private List<Component> componentArrayList = new ArrayList<Component>();
	//增加一个叶子构件或树枝构件
	public void add(Component component){
		this.componentArrayList.add(component);
	}
	//删除一个叶子构件或树枝构件
	public void remove(Component component){
		this.componentArrayList.remove(component);
	}
	//获得分支下的所有叶子构件和树枝构件
	public List<Component> getChildren(){
		return this.componentArrayList;
	}
}

步骤四:定义客户端类

public class Client {
	public static void main(String[] args) {
		//创建一个根节点
		Composite root = new Composite();
		root.operation();
		//创建一个树枝构件
		Composite branch = new Composite();
		//创建一个叶子节点
		Leaf leaf = new Leaf();
		//建立整体
		root.add(branch);
		branch.add(leaf);
	}

	//通过递归遍历树
	public static void showTree(Composite root){
		for(Component c:root.getChildren()){
			if(c instanceof Leaf){ //叶子节点
				c.operation();
			}else{ //树枝节点
				showTree((Composite)c);
			}
		}
	}
}


方案二:透明形式的组合模式

在Component中声明所有来管理子对象的方法(如 add,remove,getChild)。那么子类的 Leaf 和 Composit 对于外界没有区别,它们具备完全一致的接口。但是需要将在 Leaf 类中存在的管理子对象的方法屏蔽掉,并且抛出适当的异常。

在这里插入图片描述

步骤一:定义抽象控件

public abstract class Component {
	//个体和整体都具有
	public void operation(){
		//编写业务逻辑
	}
	//增加一个叶子构件或树枝构件
	public abstract void add(Component component);
	//删除一个叶子构件或树枝构件
	public abstract void remove(Component component);
	//获得分支下的所有叶子构件和树枝构件
	public abstract List<Component> getChildren();
}


步骤二:定义 Leaf 控件

public class Leaf extends Component {
	
	/*
	* 可以覆写父类方法
	* public void operation(){
	*
	* }
	*/
	public void add(Component component){
		//空实现,抛出“不支持请求”异常
        throw new UnsupportedOperationException();
	}

	public void remove(Component component){
		//空实现,抛出“不支持请求”异常
        throw new UnsupportedOperationException();
	}

	public List<Component> getChildren(){
		//空实现,抛出“不支持请求”异常
        throw new UnsupportedOperationException();
	}
}



步骤三:定义 Composite 控件

public class Composite extends Component {
	//构件容器
	private List<Component> componentArrayList = new ArrayList<Component>();
	//增加一个叶子构件或树枝构件
	public void add(Component component){
		this.componentArrayList.add(component);
	}
	//删除一个叶子构件或树枝构件
	public void remove(Component component){
		this.componentArrayList.remove(component);
	}
	//获得分支下的所有叶子构件和树枝构件
	public List<Component> getChildren(){
		return this.componentArrayList;
	}
}

步骤四:定义客户端类

public class Client {

	public static void main(String[] args) {
		//创建一个根节点
		Composite root = new Composite();
		root.operation();
		//创建一个树枝构件
		Composite branch = new Composite();
		//创建一个叶子节点
		Leaf leaf = new Leaf();
		//建立整体
		root.add(branch);
		branch.add(leaf);
	}

	//通过递归遍历树
	public static void showTree(Component root){
		for(Component c:root.getChildren()){
			if(c instanceof Leaf){ //叶子节点
				c.operation();
			}else{ //树枝节点
				showTree(c);
			}
		}
	}
}



安全模式和透明模式的区别

  1. 安全模式在抽象组件中只定义一些默认的行为或属性,它是把树枝节点和树叶节点彻底分开;透明模式是把用来组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,因此需要做抛出异常的处理。

  2. 安全模式与依赖倒置原则冲突;透明模式的好处就是它基本遵循了依赖倒置原则,方便系统进行扩展。

  3. 安全模式在遍历树形结构的的时候需要进行强制类型转换;在透明模式下,遍历整个树形结构是比较容易的,不用进行强制类型转换。

猜你喜欢

转载自blog.csdn.net/starter_____/article/details/89259203
今日推荐