【设计模式】学习笔记13:组合模式(Composite)
本文出自 http://blog.csdn.net/shuangde800
认识组合模式
上一篇中,我们可以用迭代器来实现遍历一个集合(数组,ArrayList, Vector, HashTable等)。
假设有这样一种集合结构i:餐厅里有一份菜单,菜单里面还有子菜单,其实就是一个树形的结构
那么,之前的迭代器就不能用了。
我们需要新的设计:
1. 需要某种树形结构,可以容纳菜单,子菜单和菜单项
2. 需要确定能够在每个菜单的各个项之间游走,而且至少要像现在用迭代器一样方便
3. 我们也需要更有弹性地在菜单之间游走。比方说,可能只需要遍历甜点菜单,或者可以遍历餐厅的整个菜单
定义组合模式
组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合
有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。
以嵌套菜单为例,这个模式能够创建一个树形结构,在同一个结构中处理嵌套菜单和菜单项组。通过将菜单和项放在相同的结构中,我们创建了一个“整体/部分”层次结构,即由菜单和菜单项组成的对象树。但是可以将它视为一个整体,像是一个丰富的大菜单。
一旦有了丰富的“大菜单”,我们就可以用这个模式来“统一处理个别对象和组合对象”。
这意味着,如果我们有了一个树形结构的菜单,子菜单和可能还带有菜单项的子菜单,那么任何一个菜单都是一种“组合”。因为它既可以包含其他菜单,也可以包含菜单项。我们可以忽略对象组合和个别对象之间的差别。
使用组合模式,只要写出简单的代码,就能够对整个菜单结构应用相同的操作!
利用组合设计菜单
1. 实现菜单组件
// 菜单组件的抽象类
// 菜单组件的角色是为叶子节点和组合节点提供一个共同的接口
// 所有的组件都必须实现MenuComponent接口
// 但是叶节点和在组合节点的角色不同,所以有些方法可能不适合某些节点
// 面对这种情况,最好抛出运行时异常
public abstract class MenuComponent { public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
} public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
} public void print() {
throw new UnsupportedOperationException();
}
}
2. 实现菜单项
// 这是组合类图的叶类,这里是菜单项
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price; public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
} public String getName() {
return name;
} public String getDescription() {
return description;
} public double getPrice() {
return price;
} public boolean isVegetarian() {
return vegetarian;
} // 这里和之前的实现不一样
// 我们覆盖了print()方法
// 对菜单项来说,此方法会打印出完整的菜单条目
// 包括名字,描述,价格等
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
3. 实现组合菜单
public class Menu extends MenuComponent {
// 菜单可以有任意数目的儿子
// 且这些儿子必须属于MenuComonent类型
// 在内部ArrayList里记录它们
ArrayList menuComponents = new ArrayList();
String name;
String description;
// 和菜单项不一样,这里只描述菜单名
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
// 这个方法添加一个子菜单或者菜单项
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
// 删除一个子菜单或者菜单项
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
// 获取一个儿子节点
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
// 递归打印菜单
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
menuComponent.print();
}
}
}
组合迭代器
组合模式的迭代器需要用递归来实现
public class CompositeIterator implements Iterator {
Stack stack = new Stack();
// 将我们需要遍历的顶层组合的迭代器传入
// 放进一个栈数据结构中
public CompositeIterator(Iterator iterator) {
stack.push(iterator);
}
// 当客户想要取得下一个元素的时候
// 我们先调用hashNext()来判断是否还有下一个元素
public Object next() {
if (hasNext()) {
Iterator iterator = (Iterator) stack.peek();
MenuComponent component = (MenuComponent) iterator.next();
if (component instanceof Menu) {
// 如果元素是菜单,我们有了另一个需要被包含进遍历中的组合
// 将它放进栈中
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}
public boolean hasNext() {
if (stack.empty()) { //如果栈空了,说明没有下一个元素了
return false;
} else {
Iterator iterator = (Iterator) stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
}
// 不支持删除
public void remove() {
throw new UnsupportedOperationException();
}
}
空迭代器
public class Menu extends MenuComponent {
// 其它部分代码不需要修改
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator()) ;
}
}
但是在菜单项中要怎样实现呢?我们知道菜单项是叶子节点,没有什么可以遍历了。
public class NullIterator implements Iterator {
public Object next() {
return null;
}
public boolean hasNext() {
return false;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
菜单项实现createIterator()方法
public class MenuItem extends MenuComponent {
// 其它部分代码不需要修改
public Iterator createIterator() {
return new NullIterator(); // 返回空迭代器
}
}
【设计模式】学习笔记13:组合模式(Composite)的更多相关文章
- 设计模式(七)组合模式Composite(结构型)
设计模式(七)组合模式Composite(结构型) 1. 概述 在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面. 例子1:就是多级树形菜单. 例子2:文件和文件夹目录 2.问题 ...
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- javascript设计模式学习之十——组合模式
一.组合模式定义及使用场景 组合模式将对象组合成树形结构,用以表示“部分—整体”的层次结构,除了用来表示树形结构之外,组合模式还可以利用对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性. ...
- 13组合模式Composite
一.什么是组合模式 Composite模式也叫组合模式,是构造型的设 计模式之一.通过递归手段来构造树形的对象结 构,并可以通过一个对象来访问整个对象树. 二.组合模式的结构 三.组合模式的角色和职责 ...
- 设计模式(八)组合模式 Composite
组合模式: 允许你将对象组合成树形结构来表现“整体/部分”层次结构.组合能让客户以一致的方式处理个别对象以及对象组合. 组合模式适用于创建复杂的对象,这个对象包含某些个别的对象以及这些对象的组合. 从 ...
- 设计模式学习笔记——Bridge 桥接模式
先说一下我以前对桥接模式的理解:当每个类中都使用到了同样的属性或方法时,应该将他们单独抽象出来,变成这些类的属性和方法(避免重复造轮子),当时的感觉是和三层模型中的model有点单相似,也就是让mod ...
- 设计模式学习笔记-Adapter模式
Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...
- Java 设计模式学习笔记1——策略模式(Duck例子)
0.假设现有工程(Duck)中遇到为类添加功能的问题,如何设计类添加新的功能? 1.利用继承提供的Duck(鸭子)的行为会导致哪些缺点? (1)代码在多个子类中重复 (2)很多男知道所有鸭子的全部行为 ...
- Java-马士兵设计模式学习笔记-装饰者模式
Java装饰者模式简介 一.假设有一个Worker接口,它有一个doSomething方法,Plumber和Carpenter都实现了Worker接口,代码及关系如下: 1.Worker.java p ...
- 研磨设计模式学习笔记2--外观模式Facade
需求:客户端需要按照需求,执行一个操作,操作包括一个系统中的3个模块(根据配置选择是否全部执行). 外观模式优点: 客户端无需知道系统内部实现,,只需要写好配置文件,控制那些模块执行,简单易用. 外观 ...
随机推荐
- ios常用的几个动画代码
#import "MianViewController.h" #import <QuartzCore/QuartzCore.h> @interface MianVi ...
- ASP无惧上传类不能上传中文双引号文件及ASP函数InStr存在bug
ASP无惧上传类不能上传中文双引号文件及ASP函数InStr存在bug 近日发现eWebEditor V2.8 asp 版本上传文件文件名不能包含中文双引号,发现eWebEditor使用ASP“无惧上 ...
- C# Socket学习笔记二
小记:昨天咱们已经了解了Socket的通信原理,可是点对点的一次通信并不是我们想要的,那么今天那我们就继续学习异步通信,简单来说就是服务器端和客户端可以进行多次 互发信息的通信而不用担心通道会关闭.在 ...
- hdu2095 像水题的不错题 异或运算
异或运算的基础有点忘记了 先介绍一下..2个数异或 就是对于每一个二进制位进行位运算 具有2个特殊的性质 1.一个数异或本身恒等于0,如5^5恒等于0: 2.一个数异或0恒等于本身,如5^0恒等于5. ...
- VLC播放器架构剖析
VLC采用多线程并行解码架构,线程之间通过单独的一个线程控制所有线程的状态,解码器采用filter模式.组织方式为模块架构 模块简述:libvlc 是VLC的核心部分 ...
- cookie 和 session 基本使用 以及 封装
Cookie: 是一小段文本信息,用户请求页面的时候,在浏览器和服务器之间传递.用户每次访问的时候都会记录cookie,cookie里可以包含用户信息,浏览的历史记录等等:Cookie是由服务器端生成 ...
- http常见的get请求方式和set请求方式。
一.Get请求方式 以下是我写的一个用get请求方式获取api工厂中汇率的类. package com.example; import java.io.BufferedReader; import j ...
- ng-class用法
在angular中为我们提供了3种方案处理class: 1:scope变量绑定.这种方案不推荐,因为scope里最好处理业务逻辑,不去管渲染的事.2:字符串数组形式.3:对象key/value处理. ...
- JS Encoding and Decoding
//charator Str to Hex function strToHex(str) { var rs = ""; for (var i = 0; i < str.len ...
- 找出数组中特定和数字下标(JAVA)
比如: 输入: numbers={2, 7, 11, 15}, target=9 输出: index1=1, index2=2 public class _003TwoSum { public sta ...