java设计模式之组合模式

动机

  • 对于树形结构,当容器对象(如文件夹)的某
    一个方法被调用时,将遍历整个树形结构,寻
    找也包含这个方法的成员对象(可以是容器对
    象,也可以是叶子对象,如子文件夹和文件)
    并调用执行(递归调用)
  • 由于容器对象和叶子对象在功能上的区别,在
    使用这些对象的客户代码中必须有区别地对待
    容器对象和叶子对象,而实际上大多数情况下
    客户希望一致地处理它们
    ,因为对于这些对象
    的区别对待将会使得程序非常复杂
  • 组合模式描述了如何将容器对象和叶子对象进
    行递归组合,使得用户在使用时无须对它们进
    行区分,可以一致地对待容器对象和叶子对象

定义

组合多个对象形成树形结构以表示“整体-部分”
的结构层次。组合模式使客户对单个对象(即
叶子对象)和组合对象(即容器对象)的使用
具有一致性

结构

组合模式的设计类图

组合模式包含的角色:

  • Component—抽象构件角色
  • 象角色,它给参加组合的对象规定一个接口,这个角色给
    出共有的接口及其默认行为
  • Leaf—叶子构件角色
  • 代表参加组合的树叶对象,一个树叶没有下级的子对象,它
    定义出参加组合的原始对象的行为
  • Composite—容器构件角色
  • 代表参加组合的有子对象的对象,给出树枝构件对象的行为
  • Composite类型的对象含有其他Component类型的对象。即:
    Composite类型的对象可以含有包含其他的容器(Composite)
    类型或叶子(Leaf)类型的对象

分析

  • 组合模式的关键是定义了一个抽象构件类
    Component,它既可以代表叶子Leaf,又可以代
    表容器Composite。客户针对Component类进行
    编程,无须知道它到底表示的是叶子还是容器,
    可以对其进行统一处理
  • 容器对象Composite与抽象构件类Component之
    间还建立一个聚合关联关系,在容器对象中既
    可以包含叶子,也可以包含容器,以此实现递
    归组合,形成一个树形结构

Component — 抽象构件角色代码

public abstract class Component
{
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract Component getChild(int i);
public abstract void operation();
}

** Leaf — 叶子构件角色代码**

public class Leaf extends Component
{
public void add(Component c)
{ // 异 常 处 理 或 错 误 提 示 }
public void remove(Component c)
{ // 异 常 处 理 或 错 误 提 示 }
public Component getChild(int i)
{ // 异 常 处 理 或 错 误 提 示 }
public void operation()
{
... ... // 实 现 代 码
}
}

Composite — 容器构件角色代码

 public class Composite extends Component {
private ArrayList list = new ArrayList();
public void add(Component c) {
list.add(c);
}
public void remove(Component c) {
list.remove(c);
}
public Component getChild(int i) {
(Component)list.get(i);
}
public void operation() {
for(Object obj:list) {
((Component)obj).operation(); // 递 归
}
}}

举例

举例:文件目录

  • 在计算机文件系统中有目录(或称文件夹),目录里
    面有文件或者子目录,在子目录里面还会有其他文件
    或子目录
  • 虽然目录和文件实际上是两种截然不同的东西,但两
    者都可放在目录里面

实现代码

  • Entity.java
package composite;

/**
 * 条目类<br>
 * 对File和Directory一视同仁
 */
public abstract class Entry {
		
	public abstract String getName(); // 取得条目名

	public abstract int getSize(); // 取得条目大小

	protected abstract void printList(String prefix); // 打印条目信息

	/** 新增条目: 当前条目为文件时,不能增加条目,为目录时才可以(重写add) */
	public Entry add(Entry entry) throws FileTreatmentException {
		throw new FileTreatmentException(); // 默认抛出自定义异常
	}

	public String toString() { // 输出格式
		return getName() + "(" + getSize() + ")";
	}
}
  • FileTreatmentException.java
package composite;

public class FileTreatmentException extends RuntimeException {
	public FileTreatmentException() {
	}

	public FileTreatmentException(String msg) {
		super(msg);
	}
}

  • File.java
package composite;

/** 文件类 */
public class File extends Entry {
	private String name;
	private int size;

	public File(String name, int size) {
		this.name = name;
		this.size = size;
	}

	public String getName() {
		return name;
	}

	public int getSize() {
		return size;
	}

	protected void printList(String prefix) {
		System.out.println(prefix + "/" + this);
	}
}
  • Directory.java
package composite;

import java.util.Iterator;
import java.util.ArrayList;

/** 目录类 */
public class Directory extends Entry {
	private String name; // 目录名称
	// ArrayList用于存放条目
	private ArrayList<Entry> directory = new ArrayList<Entry>();

	public Directory(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	/** 递归计算目录的大小 */
	public int getSize() {
		int size = 0;
		Iterator<Entry> it = directory.iterator();
		while (it.hasNext()) {
			Entry entry = it.next();
			size += entry.getSize();
		}
		return size;
	}

	/** 重写父类的add()方法 */
	public Entry add(Entry entry) {
		directory.add(entry);
		return this;
	}

	/** 递归打印目录信息 */
	protected void printList(String prefix) {
		System.out.println(prefix + "/" + this);
		Iterator<Entry> it = directory.iterator();
		while (it.hasNext()) {
			Entry entry = it.next();
			entry.printList(prefix + "/" + name);
		}
	}
}
  • Test.java
package composite;

/** 测试类 */
public class Test {
	public static void main(String[] args) {
		try {
			System.out.println("Making root entries...");
			Directory rootdir = new Directory("root");
			Directory usrdir = new Directory("usr");
			rootdir.add(usrdir);
			rootdir.printList("");
			
			System.out.println();
			
			System.out.println("Making user entries...");
			Directory zs = new Directory("ZhangShan");
			Directory ls = new Directory("LiSi");
			usrdir.add(zs);
			usrdir.add(ls);
			zs.add(new File("diary.html", 100));
			zs.add(new File("Composite.javan", 200));
			ls.add(new File("memo.tex", 300));
			rootdir.printList("");
		} catch (FileTreatmentException e) {
			e.printStackTrace();
		}
	}
}

组合模式优点

  • 可以清楚地定义分层次的复杂对象,表示对象的全
    部或部分层次,使得增加新构件也更容易
  • 客户调用简单,客户可以一致地使用组合结构或其
    中单个对象
  • 定义了包含叶子对象和容器对象的类层次结构,叶
    子对象可以被组合成更复杂的容器对象,而这个容
    器对象又可以被组合,这样不断递归下去,可以形
    成复杂的树形结构
  • 更容易在组合体内加入对象构件,客户不必因为加
    入了新的对象构件而更改原有代码

组合模式缺点

  • 使设计变得更加抽象,对象的业务规则如果很复杂,
    则实现组合模式具有很大挑战性,而且不是所有的

适用环境

  • 需要表示一个对象整体或部分层次,在具有整
    体和部分的层次结构中,希望通过一种方式忽
    略整体与部分的差异,可以一致地对待它们
  • 让客户能够忽略不同对象层次的变化,客户可
    以针对抽象构件编程,无须关心对象层次结构
    的细节
  • 对象的结构是动态的并且复杂程度不一样,但
    客户需要一致地处理它们
    方法都与叶子对象子类有关联
  • 增加新构件时可能会产生一些问题,很难对容器中
    的构件类型进行限制

应用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值