`

三十六:装饰模式

阅读更多
装饰模式(Decorator)又名包装(Wrapper)模式,装饰模式以对客户端透明的方式扩展对象的功能,是继承的一个替代方案.

一:引言
孙悟空有七十二般变化,他的每一种变化都给他带来一种附加本领。他变成鱼时,就可以到水里游泳,他变成鸟时,就可以在天上飞行,而不管悟空怎么变化,在二郎神眼里,他永远是那只猢狲.
装饰模式以对客户透明的方式动态地给一人对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同,装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
装饰模式使用原来被装饰的类的一个子类的实例,把客户端的调用委派到被装饰类,装饰模式的关键在于这种扩展是完全透明的。老孙变成的鱼相当于老孙的子类,这条鱼与外界互动要通过"委派"交给老孙本尊,由老孙本尊采取行动,尽管老孙把自己"装饰"成了鱼,在二郎神眼里,他仍然是那只猢狲。

二:装饰模式的结构
在装饰模式中的角色有:
(A)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加功能的对象
(B)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类
(C)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口
(D)具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加功能
下面给出装饰模式的示意性源代码:
package cai.milenfan.basic.test; 
import java.util.Enumeration; 
//抽象构件角色
public interface Component { 
//某个商业方法
void sampleOperation(); 
} 


package cai.milenfan.basic.test; 

/** 
  *这是一个装饰类,应该注意几点: 
  * (1)有一个私有的属性component,其数据类型是构件Component 
  * (2)接口的实现方法也值得注意,每一个实现的方法都委派给父类,但并不是单纯的委派,而是有功能的增强
  */ 
public class Decorator implements Component{ 
private Component cmponent; 
public Decorator(){} 
public Decorator(Component component){ 
this.cmponent = component; 
} 
public void sampleOperation(){ 
cmponent.sampleOperation(); 
} 
} 



package cai.milenfan.basic.test; 
//具体装饰角色
public class ConcreateComponent implements Component{ 
public ConcreateComponent(){ 
//write your code here 
} 
public void sampleOperation() { 
//write your code here 
} 
} 


package cai.milenfan.basic.test; 
//具体装饰类
public class ConcreteDecorator extends Decorator{ 
//商业方法
public void sampleOperation(){ 
super.sampleOperation(); 
} 
} 

装饰模式的对象图如下: 
new Decorator1( 
new Decorator2( 
new Decorator3( 
new ConcreteComponent(); 
) 
) 
); 


这就意味着Decorator1的对象持有一个对Decorator2对象的引用,后者则持有一个对Decorator3的引用,再后者持有一个对具体构件ConcreteComponent对象的引用,这种链式的引用关系使装饰模式看上去像是一个LinkedList.
回到齐天大圣的例子:Component的角色由孙悟空扮演,ConcreteComponent的角色属于大圣本尊,就是猢狲本人,大圣的七十二变扮演的便是Decorator角色,而ConcreteDecorator的角色便是花,鸟,鱼等七十二般变化。

四:在什么情况下使用装饰模式
(1)需要扩展一个类的功能,或给一个类增加附加责任
(2)需要动态地给一个对象增加功能,这些功能可以再动态地撤消
(3)需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实
再回到齐天大圣的例子:
首先有一个大圣本尊的实例:
齐天大圣c = new大圣本尊();
然后,你把大圣"装饰"成一个雀儿:
齐天大圣bird = new雀儿(c);
由于"大圣本尊"是ConcreteComponent类,因此可以调用默认的构造子创建实例,而"雀儿"是装饰类,要装饰的是"大圣本尊",也即一个"猢狲"实例,换言之,上面的java语句把"猢狲"变成了"雀儿"(把雀儿的功能加到了"猢狲"身上)。由于多态性原则,"雀儿"构造子可以接受任何"齐天大圣"的子类的实例,这也就意味着可以把任何大圣的化身装饰成"雀儿",比如:
齐天大圣c = new大圣本尊();
齐天大圣fish = new鱼儿(c);
齐天大圣bird = new雀儿(fish);

五:使用装饰模式的优点和缺点
(1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性,它允许系统动态地决定"贴上"一个需要的"装饰",或者除掉一个不需要的"装饰",继承关系则不同,继承关系是静态的,它在系统运行就决定了。
。 。 。 。

六:模式实现的讨论
大多数情况下,装饰模式的实现都比本节的定义中给出的示意性实现要简单,对模式进行简化时需要注意以下情况:
(1)一个装饰类的接口必须与被装饰类的接口相容:ConcreteDecorator类必须继承自一个共同的父类Component
(2)尽量保持Component作为一个"轻"类:这个类的责任是为各个ConcreteDecorator类提供共同的接口,因此它应当重在提供接口而不是存储数据。在示例中Component是一个java接口,而在实际工作中,它可以是一个抽象类或者是一个具体类,此时,就应当注意不要把太多的逻辑和状态放在Component类里。
(3)如果只有一个ConcreteComponent类而没有抽象的Component类(接口),那么Decorator类经常可以是ConcreteComponent的一个子类,这样的话ConcreteComponent就要扮演双重角色。
(4)如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类,甚至只有两个ConcreteDecorator类的情况下都可以这样做,但是如果ConcreteDecorator类的数目大于三的话,使用一个单独的Decorator类来区分抽象和具体的责任就是必要的了。

透明性要求:
最后指出一个重要问题,通常叫做针对抽象编程,装饰模式对客户端的透明性要求程序不要声明一个ConcreteDecorator类型的变量,而应当声明一个Component类型的变量。用孙悟空的例子来说,必须永远把孙悟空的所有变化都当成孙悟空来对待,而如果把他变成的雀儿当成雀儿,那就被老孙骗了!换言之,下面的做法是对的:
齐天大圣c = new大圣本尊();
而下面的做法是不对的:
大圣本尊c = new大圣本尊();,这就意味着在ConcreteDecorator里不可以有Component里所没有的方法,为什么呢?如果在ConcreteDecorator里面有一个新的方法newMethod(),那么,客户端怎么调用这个newMethod()呢?记住客户端只有Component接口,而这个接口里并没有newMethod()方法。

半透明的装饰模式
纯粹的装饰模式很难找到,装饰模式的用意是在不改变接口的前提下,增强所考虑的类的性能,在增强性能的时候,往往要建立新的公开的方法,即便是在孙大圣的系统里,也需要新的方法。比如齐天大圣类并没有飞行能力,而雀儿有,这就意味着雀儿应当有一个fly()方法,再比如,齐天大圣并没有你的游泳能力,而鱼儿有,这就意味着在鱼儿类里应当有一个新的swim()方法。这就导致了大多数的装饰模式的实现都是"半透明"的,换言之,允许装饰模式改变接口,增加新的方法,这意味着客户端可以声明ConcreteDecorator类型的变量,从而可以调用ConcreteDeconrator类中才有的方法:
齐天大圣c = new大圣本尊();
雀儿bird = new雀儿(c);
bird.fly();
但是,只要客户端不需要调用这些属于装饰的方法,而只调用属于Component的方法,那么装饰模式就仍然等同于透明的

七:装饰模式与其他模式的关系
(1)装饰模式与适配器模式的区别和联系
它们都有一个别名,即包装(Wrapper)模式,但是这两个模式是很不一样的。适配器模式的用意是要改变所考虑的对象的接口而不一定改变对象的性能,而装饰模式的用意是要保持接口,从而增强所考虑对象的性能
(2)装饰模式将一个东西的表皮换掉,而保持它的内心,策略模式恰好相反,它在保持接口不变的情况下,使得算法可以互换,装饰模式的实现要求Component类尽量地"轻",而策略模式要求抽象策略类尽量"重"。
(3)装饰模式与合成模式的关系
装饰模式常常用在合成模式的行为扩展上,使用继承关系扩展合成模式的行为很困难,如果仅仅对抽象构件(Component)类还是合成类(Composite)类或者树叶类(Leaf)类使用继承,会导致多态性被破坏

八:一个例子GrepReader---半透明的装饰模式的应用
功能描述:Grep是Unix操作系统中的命令,可以用来处理对流的搜索,由于Grep给出的结果仍然是流,因此可以作为过滤器在任何一条管道线中使用,比如命令Grep BMW file在文件file中搜索所有含有BMW字样的行,并显示出来.本例就用java的i/o包来实现一个简单的搜索器,叫做Grep,进行类似的搜索.
(1)宏观设计
首先把程序进行功能的划分,也即宏观设计。这里采用MVC模式:
(A)模型(Model):即GrepReader类,是一个对输入的文件流进行处理的构件,真正的搜索就发生在这里,这是一个处理char流的构件.
(B)视图(View):即GrepView类,是一个简单的数据输出工具,它只是把数据打印到屏幕上
(C)控制器(Control):即Grep类,是系统的控制中心.
(2)微观设计
即考虑每个类的大概的实现是怎么样的
(3)源码
package cai.milenfan.basic.test; 

import java.io.BufferedReader; 
import java.io.FileReader; 
import java.io.FilterReader; 
import java.io.IOException; 
import java.io.Reader; 

public class GrepReader extends FilterReader{ 
protected String substring; 
protected BufferedReader in; 
private int lineNumber; 
protected GrepReader(FileReader in,String substring) { 
super(in); 
this.in = new BufferedReader(in); 
this.substring = substring; 
lineNumber =0; 
} 
public final String readLine()throws IOException{ 
String line; 
do{ 
line = in.readLine(); 
lineNumber ; 
}while(line!=null&&line.indexOf(substring)==-1);
return line; 
} 
public int getLineNo(){ 
return lineNumber; 
} 
} 



package cai.milenfan.basic.test; 

import java.io.PrintStream; 

public class GrepView { 
PrintStream out; 
public GrepView(){ 
out = System.out; 
} 
public void println(String line){ 
out.println(line); 
} 
} 

package cai.milenfan.basic.test; 
import java.io.*; 
public class Grep { 
static GrepReader g; 
private static GrepView gv = new GrepView(); 
public static void main(String[] args){ 
String line; 
if(args.length<=1){ 
gv.println("Usage:java Grep "); 
gv.println(" no regexp"); 
gv.println(" files to be searched in"); 
System.exit(1); 
} 
gv.println("\nGrep:搜索" args[0] "文件" args[1]);
gv.println("文件号和行号\r\t下面的行里含有所搜索的字符串\n"); 

try { 
g = new GrepReader(new FileReader(args[0]),args[1]); 
for(;;){ 
line = g.readLine(); 
if(line==null){ 
break; 
} 
gv.println(args[1] ":" g.getLineNo() ";\t" line); 
} 
g.close(); 
} catch (Exception e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
} 
} 
} 


//运行: java Grep C:\\AJAX.txt Ajax
//如果某行以Ajax开头,程序就会打印出这一行的文字.
//在eclipse里为main方法传参数:run-->run configulation->main选项里选择要运行的类,argument里的program argument里输入参数后apply->run

九:一个发票系统(跳过)
分享到:
评论

相关推荐

    Head First 设计模式 JAVA源码

    第八讲:装饰模式 第九讲:策略模式 第十讲:观察者模式 第十一讲:享元模式 第十二讲:代理模式 第十三讲:外观模式 第十四讲:组合模式 第十五讲:桥接模式 第十六讲:适配器模式 第十七讲:解释器模式 ...

    设计模式PPT.rar

    01第一讲简单工厂模式 02第二讲工厂方法模式 03第三讲抽象工厂模式 04第四讲工厂模式在开发中的运用 05第五讲单例模式 06第六讲原型模式 07第七讲建造者模式 08第八讲装饰模式 09第九讲策略模式 10第十讲观察者模式 ...

    设计模式-7种开发语言(C#、JAVA、JavaScript、C++、Python、Go、PHP).pdf

    第十六节 装饰器模式 第十七节 迭代器模式 第十八节 中介者模式 第十九节 备忘录模式 第二十节 状态模式 第二十一节 访问者模式 第二十二节 原型模式 第二十三节 享元模式 第二十四节 责任链模式 第二十五节 解释器...

    C++设计模式

    (三)装饰模式 7 (四)代理模式 9 (五)工厂方法模式 11 (六)原型模式 13 (七)模板方法模式 15 迪米特法则 16 (八)外观模式 16 (九)建造者模式(生成器模式) 19 (十)观察者模式 23 (十一)抽象工厂...

    php设计模式介绍

    《PHP设计模式介绍》导言 《PHP设计模式介绍》第一章 编程惯用法 《PHP设计模式介绍》第二章 值对象模式 《PHP设计模式介绍》第...《PHP设计模式介绍》第十二章 装饰器模式 《PHP设计模式介绍》第十三章 适配器模式

    [qc成果]外墙外保温装饰一体板施工质量控制.doc

    一、前言………………………………………………………………………3二、QC小组简介………………………………………………………………3三、选题理由及选题…………………………………………………………5四、现状...

    Java基础知识点总结.docx

    单例设计模式:★★★★★ 156 工厂模式★★★★★ 159 抽象工厂模式★★★★★ 163 建造者模式 170 原型模式 177 适配器模式 182 桥接模式 188 过滤器模式 192 组合模式 193 装饰器模式★★★★★ 196 外观模式 201...

    asp.net知识库

    ASP.NET运行模式:PageHandlerFactory 利用搜索引擎引用来高亮页面关键字 网站首页的自动语言切换 应用系统的多语言支持 (一) 应用系统的多语言支持 (二) 自动返回上次请求页面(小技巧) ASP.NET 2.0 控件 ASP...

    C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial 前38节)

    C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十六) 地图自定义切片与导出 C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十七) 地图自适应区域加载 C#开发WPF/Silverlight动画...

    C#开发WPF/Silverlight动画及游戏系列教程(Part1)

    (三十六)地图自定义切片与导出 (三十七)地图自适应区域加载 (三十八)地图间的传送与切换 (三十九)向Silverlight移植① (四十)向Silverlight移植② (四十一)制作精美的Mini地图① (四十二)制作精美的Mini地图② (四十...

    Guava 16.0 API (CHM格式)

     十三. Reflection: Guava 的 Java 反射机制工具类。 如果文件打开看不到右边的内容,是因为你的操作系统为了安全对下载的chm文件进行了锁定,只需要在打开前右键单击该chm文件选择“属性”,然后在“常规”选项卡...

    python cookbook(第3版)

    3.4 二八十六进制整数 3.5 字节到大整数的打包与解包 3.6 复数的数学运算 3.7 无穷大与NaN 3.8 分数运算 3.9 大型数组运算 3.10 矩阵与线性代数运算 3.11 随机选择 3.12 基本的日期与时间转换 3.13 计算...

    java面试题库2021.pdf

    目录 一、 JavaSE 部分 1、 Java 基础 ①Java 基础部分(基本语法, Java ...②装饰模式 ③适配器模式 2、 创建型模式 ①单例模式 3、 行为型模式 ①策略模式 ②观察者模式 4、 所有模式汇总 十、 场景题 十一、 UML

    react-flowing-gold:react项目

    React 项目目录一、二、三、四、五、六、七、八、Redux-saga状态共享九、结尾十、Github链接一、技术栈详情可参阅 package.jsonYarnReact 16.9.0Redux-sagaReact RouterAxios 请求库Webpack 4.0+ES6 + BabelAntd项目...

    CATIA V5操作手册

    第十一节 二维制图模式与二、三维建模 14 第十二节 变换特征 15 第二章 点的生成 16 第三章 动态草绘器 DYNAMIC SKETCHER 18 第四章 实体生成及修改 22 第一节 产生基于草图的特征 22 第二节 产生装饰特征 CREATING ...

    手机开发完整流程

    一,主板方案的确定 二,设计指引的制作 三,手机外形的确定 四,结构建模 1.... 2.... 3.... 4.... 5....六,结构设计 ... 1.... 2.... 3.... 4.上壳装饰五金片的固定结构 ...十三,试模及改模 十四,试产 十五,量产

    一款完整的手机结构设计过程

    详细介绍手机的结构的开发流程 一,主板方案的确定 二,设计指引的制作 三,手机外形的确定 四,结构建模 1.资料的收集 2.构思拆件 3.外观面的绘制 4.初步拆件 ...十三,试模及改模 十四,试产 十五,量产

    浅谈电子商务和网络营销的关系.rar

    商城一楼可能是一级品牌,然后二楼是女士服饰,三楼男士服饰,四楼运动/装饰,五楼手机数码,六楼特价……将N个品牌专卖店装进去,这就是商城。而后面的 淘宝商城 、365商城等也自然是这个形式,跟传统无异,它有...

    java面试题以及技巧

    │ │ ├─考前预测的三套题 │ │ │ Desktop_.ini │ │ │ Mock Exam - JCHQ - SCJP Exam 2.doc │ │ │ Mock Exam - JCHQ - SCJP Exam 2.pdf │ │ │ Mock Exam - JCHQ - SCJP Exam 3.pdf │ │ │ SCJP_...

    java面试题目与技巧1

    │ │ ├─考前预测的三套题 │ │ │ Desktop_.ini │ │ │ Mock Exam - JCHQ - SCJP Exam 2.doc │ │ │ Mock Exam - JCHQ - SCJP Exam 2.pdf │ │ │ Mock Exam - JCHQ - SCJP Exam 3.pdf │ │ │ SCJP_...

Global site tag (gtag.js) - Google Analytics