装饰器(Decorator)模式

Decorator设计模式是典型的结构型模式(在GOF的那本模式的Bible中将模式分为:1.创建型模式;2.结构型模式;3.行为模式三种)。它的主要用意是:动态地为对象添加一些额外的功能。(记住上面两种颜色的词汇,理解装饰器模式的精髓所在!)下面是GOF的《Element of reusable Object-Oriented Software》中对Decorator用意的概述:

Decorator Pattern――Attaches additional responsibilities to an object dynamically . Decorators provide a flexible alternative to subclassing for extending functionality .

1 何时需要使用装饰器模式

GOF的那本Bible中关于装饰器模式列举的是一个文本组件与边框的例子(在这里我就不举了,主要是因为我会在书中举一个相似的,但却非常有说服力的例子,它对Swing中的某些本来应该使用Decorator却没有使用的对象的改进。同时会提出内包装、外包装的概念。看到这个例子后大家仔细体会吧!通过例子告诉大家一点:任何设计不是一成不变的、模式的应用是极其灵活的……)。下面我举一个“三明治”的例子!

很多人都吃过三明治(我除外!“没吃过猪肉,俺可听过猪叫”),都会知道三明治必不可少的是两块面包片,然后可以在夹层里加上蔬菜、沙拉、咸肉等等,外面可以涂上奶油之类的。假如现在你要为一个三明治小店构造一个程序,其中要设计各种三明治的对象。可能你已经创建了一个简单的Sandwich对象,现在要产生带蔬菜的就是继承原有的Sandwich添加一个蔬菜的成员变量,看起来很“正点”的做法,以后我还要带咸肉的、带奶油的、带蔬菜的又分为带青菜的、带芹菜的、生菜的……还是一个一个继承是吧!假如我们还需要即带蔬菜又带其它肉类,设置我们还要求这些添加成分的任意组合,那你就慢慢继承吧!

读过几年书的会下面这个算术,我们有n种成分,在做三明治的时候任意搭配,那么有多少种方案呢?!算算吧!你会有惊人的发现。N种成分,什么都不要是Cn0种方案吧!要1种是Cn1吧!…..要n种是Cnn吧!加起来不就是吗?Cn0+Cn1+……+Cnn-1+Cnn还不会啊!牛顿莱布尼兹公式记得吧!(可惜Word的公式编辑器安装不了)总共2的n次方案。有可能前面10天写了K个类,老板让你再加一种成分你就得再干10天,下一次再加一种你可得干20天哦!同时你可以发现你的类库急剧地膨胀!(老板可能会说你:XXX前K天你加了n个成分,怎么现在这么不上进呢?后K天只加了1个成分啊?!!可能你会拿个比给老板算算,老板那么忙会睬你吗?!有可能你的老板会说:不管怎么样我就要你加,K天你还给我加n个成分!!呵呵,怎么办啊!跳槽啊!跳槽了也没人要你!!人家一看就知道你没学设计模式)。下面我们就使用装饰器模式来设计这个库吧!下图是我们的设计图:

下面是以上各个类的意义:

1.         Ingredient(成分):所有类的父类,包括它们共有的方法,一般为抽象类且方法都有默认的实现,也可以为接口。它有Bread和Decorator两个子类。这种实际不存在的,系统需要的抽象类仅仅表示一个概念,图中用红色表示。

2.         Bread(面包):就是我们三明治中必须的两片面包。它是系统中最基本的元素,也是被装饰的元素,和IO中的媒质流(原始流)一个意义。在装饰器模式中属于一类角色,所以其颜色为紫色。

3.         Decorator(装饰器):所有其它成分的父类,这些成分可以是猪肉、羊肉、青菜、芹菜。这也是一个实际不存在的类,仅仅表示一个概念,即具有装饰功能的所有对象的父类。图中用蓝色表示。

4.         Pork(猪肉):具体的一个成分,不过它作为装饰成分和面包搭配。

5.         Mutton(羊肉):同上。

6.         Celery(芹菜):同上。

7.         Greengrocery(青菜):同上。

总结一下装饰器模式中的四种角色:1.被装饰对象(Bread);2.装饰对象(四种);3.装饰器(Decorator);4.公共接口或抽象类(Ingredient)。其中1和2是系统或者实际存在的,3和4是实现装饰功能需要的抽象类。

写段代码体会其威力吧!(程序很简单,但是实现的方法中可以假如如何你需要的方法,意境慢慢体会吧!)

//Ingredient.java

public abstract class Ingredient {

public abstract String getDescription();

public abstract double getCost();

public void printDescription(){

System.out.println(" Name      "+ this.getDescription());

System.out.println(" Price RMB "+ this.getCost());

}

}

所有成分的父类,抽象类有一个描述自己的方法和一个得到价格的方法,以及一个打印自身描述和价格的方法(该方法与上面两个方法构成模板方法哦!)

//Bread.java

public class Bread extends Ingredient {

private String description ;

public Bread(String desc){

this.description=desc ;

}

public String getDescription(){

return description ;

}

public double getCost(){

return 2.48 ;

}

}

面包类,因为它是一个具体的成分,因此实现父类的所有的抽象方法。描述可以通过构造器传入,也可以通过set方法传入。同样价格也是一样的,我就很简单地返回了。

//Decorator.java

public abstract class Decorator extends Ingredient {

Ingredient ingredient ;

public Decorator(Ingredient igd){

this.ingredient = igd;

}

public abstract String getDescription();

public abstract double getCost();

}

装饰器对象,所有具体装饰器对象父类。它最经典的特征就是:1.必须有一个它自己的父类为自己的成员变量;2.必须继承公共父类。这是因为装饰器也是一种成分,只不过是那些具体具有装饰功能的成分的公共抽象罢了。在我们的例子中就是有一个Ingredient作为其成员变量。Decorator继承了Ingredient类。

//Pork.java

public class Pork extends Decorator{

public Pork(Ingredient igd){

super(igd);

}

public String getDescription(){

String base = ingredient.getDescription();

return base +"\n"+"Decrocated with Pork !";

}

public double getCost(){

double basePrice = ingredient.getCost();

double porkPrice = 1.8;

return        basePrice + porkPrice ;

}

}

具体的猪肉成分,同时也是一个具体的装饰器,因此它继承了Decorator类。猪肉装饰器装饰可以所有的其他对象,因此通过构造器传入一个Ingredient的实例,程序中调用了父类的构造方法,主要父类实现了这样的逻辑关系。同样因为方法是具体的成分,所以getDescription得到了实现,不过由于它是具有装饰功能的成分,因此它的描述包含了被装饰成分的描述和自身的描述。价格也是一样的。价格放回的格式被装饰成分与猪肉成分的种价格哦!

从上面两个方法中我们可以看出,猪肉装饰器的功能得到了增强,它不仅仅有自己的描述和价格,还包含被装饰成分的描述和价格。主要是因为被装饰成分是它的成员变量,因此可以任意调用它们的方法,同时可以增加自己的额外的共同,这样就增强了原来成分的功能。

//Mutton.java

public class Mutton extends Decorator{

public Mutton(Ingredient igd){

super(igd);

}

public String getDescription(){

String base = ingredient.getDescription();

return base +"\n"+"Decrocated with Mutton !";

}

public double getCost(){

double basePrice = ingredient.getCost();

double muttonPrice = 2.3;

return        basePrice + muttonPrice ;

}

}

羊肉的包装器。

//Celery.java

public class Celery extends Decorator{

public Celery(Ingredient igd){

super(igd);

}

public String getDescription(){

String base = ingredient.getDescription();

return base +"\n"+"Decrocated with Celery !";

}

public double getCost(){

double basePrice = ingredient.getCost();

double celeryPrice =0.6;

return        basePrice + celeryPrice ;

}

}

芹菜的包装器。

//GreenGrocery.java

public class GreenGrocery extends Decorator{

public GreenGrocery (Ingredient igd){

super(igd);

}

public String getDescription(){

String base = ingredient.getDescription();

return base +"\n"+"Decrocated with GreenGrocery  !";

}

public double getCost(){

double basePrice = ingredient.getCost();

double greenGroceryPrice = 0.4;

return        basePrice + greenGroceryPrice ;

}

}

青菜的包装器。

下面我们就领略装饰器模式的神奇了!我们有一个测试类,其中建立夹羊肉的三明治、全蔬菜的三明治、全荤的三明治。(感觉感觉吧!很香的哦!)

public class DecoratorTest{

public static void main(String[] args){

Ingredient compound = new Mutton(new Celery(new Bread("Master24's Bread")));

compound.printDescription();

compound = new Celery(new GreenGrocery(new Bread("Bread with milk")));

compound.printDescription();

compound = new Mutton(new Pork(new Bread("Bread with cheese")));

compound.printDescription();

}

}

以上就是一个简单的装饰器类!假如你对想中国式的吃法,可以将加入馒头、春卷皮、蛋皮……夹菜可以为肉丝……突然想到了京酱肉丝。

2 装饰器模式的结构

在谈及软件中的结构,一般会用UML图表示(UML和ANT、JUnit等都是软件设计中基本的工具,会了没有啊!)。下面是一个我们经常看到的关于Decorator模式的结构图。

1.      Component就是装饰器模式中公共方法的类,在装饰器模式结构图的顶层。

2.      ConcreateComponent是转换器模式中具体的被装饰的类,IO包中的媒体流就是此种对象。

3.      Decorator装饰器模式中的核心对象,所有具体装饰器对象的父类,完成装饰器的部分职能。在上面的例子中Decorator类和这里的对应。该类可以只做一些简单的包裹被装饰的对象,也可以还包含对Component中方法的实现……他有一个鲜明的特点:继承至Component,同时包含一个Component作为其成员变量。装饰器模式动机中的动态地增加功能是在这里实现的。

4.      ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰器对象,他们完成具体的装饰功能。装饰功能的实现是通过调用被装饰对象对应的方法,加上装饰对象自身的方法。这是装饰器模式动机中的添加额外功能的关键。

从上面图中你可能还会发现:ConcreteDecoratorA和ConcreteDecoratorB的方法不一样,这就是一般设计模式中谈及装饰器模式的“透明装饰器”和“不透明装饰器”。“透明装饰器”就是整个Decorator的结构中所有的类都保持同样的“接口”(这里是共同方法的意思),这是一种极其理想的状况,就像餐饮的例子一样。现实中绝大多数装饰器都是“不透明装饰器”,他们的“接口”在某些子类中得到增强,主要看这个类与顶层的抽象类或者接口是否有同样的公共方法。IO中的ByteArrayInputStream就比Inputstrem抽象类多一些方法,因此IO中的装饰器是一个“不通明装饰器”。下面是IO中输入字节流部分的装饰器的结构图。

1.         InputStream是装饰器的顶层类,一个抽象类!包括一些共有的方法,如:1.读方法――read(3个);2.关闭流的方法――close;3.mark相关的方法――mark、reset和markSupport;4.跳跃方法――skip;5.查询是否还有元素方法――available。图中红色的表示。

2.         FileInputStream、PipedInputStream…五个紫色的,是具体的被装饰对象。从他们的“接口”中可以看出他们一般都有额外的方法。

3.         FilterInputStream是装饰器中的核心,Decorator对象,图中蓝色的部分。

4.         DataInputStream、BufferedInputStream…四个是具体的装饰器,他们保持了和InputStream同样的接口。

5.         ObjectInputStream是IO字节输入流中特殊的装饰器,他不是FilterInputStream的子类(不知道Sun处于何种意图不作为FileterInputStream的子类,其中流中也有不少的例子)。他和其他FilterInputStream的子类功能相似都可以装饰其他对象。

IO包中不仅输入字节流是采用装饰器模式、输出字节流、输入字符流和输出字符流都是采用装饰器模式。关于IO中装饰器模式的实现可以通过下面的源代码分析从而了解细节。

3.小结

Java中的IO是明显的装饰器模式的运用。FilterInputStream,FilterOutputStream,FilterRead,FilterWriter分别为具体装饰器的父类,相当于Decorator类,它们分别实现了InputStream,OutputStream,Reader,Writer类(这些类相当于Component,是其他组件类的父类,也是Decorator类的父类)。继承自InputStream,OutputStream,Reader,Writer这四个类的其他类是具体的组件类,每个都有相应的功能,相当于ConcreteComponent类。而继承自FilterInputStream,FilterOutputStream,FilterRead,FilterWriter这四个类的其他类就是具体的装饰器对象类,即ConcreteDecorator类。通过这些装饰器类,可以给我们提供更加具体的有用的功能。如FileInputStream是InputStream的一个子类,从文件中读取数据流,BufferedInputStream是继承自FilterInputStream的具体的装饰器类,该类提供一个内存的缓冲区类保存输入流中的数据。我们使用如下的代码来使用BufferedInputStream装饰FileInputStream,就可以提供一个内存缓冲区来保存从文件中读取的输入流。

[java] view plaincopy
  1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); //其中file为某个具体文件的File或者FileDescription对象

在以下两种情况下可以考虑使用装饰器模式:

(1)需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责。

(2)如果不适合使用子类来进行扩展的时候,可以考虑使用装饰器模式。

java设计模式--装饰器设计模式(javaIO设计使用)相关推荐

  1. 结构设计模式 - 装饰器设计模式

    结构设计模式 - 装饰设计模式 装饰器设计模式用于在运行时修改对象的功能.同时,同一类的其他实例不会受此影响,因此单个对象将获得已修改的行为.装饰器设计模式是结构设计模式之一(如Adapter Pat ...

  2. 学习笔记:设计模式-装饰器设计模式(JAVA)

    概念 装饰器设计模式,也叫包装设计模式,他是作为现有类的一个包装,允许向一个现有的对象添加新的功能,同时又不改变其结构. 给对象添加功能,一般两种方式,继承或者组合,将一个类的对象嵌入到另一个对象中, ...

  3. java学习--装饰器设计模式

    装饰器设计模式 例一:实现对声音的的放大功能 源码: package me.sgyz.study02;/***实现放大器对声音的的放大功能* @ 洛尘曦**/public class Decorate ...

  4. 装饰着模式示例_装饰器设计模式示例

    装饰着模式示例 本文是我们名为" Java设计模式 "的学院课程的一部分. 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们. 您将了解模式如此重要的原 ...

  5. java设计模式之装饰模式_Java中的装饰器设计模式

    java设计模式之装饰模式 装饰器设计模式允许在运行时将附加职责或行为动态附加到对象. 它是一种结构模式,利用聚合来组合这些行为. 在本教程中,我们将学习实现装饰器模式. UML图: 让我们从装饰器模 ...

  6. Java中的装饰器设计模式

    装饰器设计模式允许在运行时将附加职责或行为动态附加到对象. 它是一种结构模式,利用聚合来组合这些行为. 在本教程中,我们将学习实现装饰器模式. UML图: 让我们从装饰器模式的UML表示开始: Con ...

  7. java 设计模式 示例_Java示例中的装饰器设计模式

    java 设计模式 示例 Decorator design pattern is used to modify the functionality of an object at runtime. A ...

  8. lambda设计模式_使用lambda的装饰器设计模式

    lambda设计模式 随着Java中lambda的出现,我们现在有了一个新工具,可以更好地设计我们的代码. 当然,第一步是使用流,方法引用和Java 8中引入的其他简洁功能. 展望未来,我认为下一步是 ...

  9. 用装饰器设计模式装饰

    装饰图案是广泛使用的结构图案之一. 此模式在运行时动态更改对象的功能,而不会影响对象的现有功能. 简而言之,此模式通过包装将附加功能添加到对象. 问题陈述: 想像一下我们有一个比萨饼,该比萨饼已经用番 ...

  10. Go 设计模式 - 装饰器模式

    装饰模式使用对象组合的方式动态改变或增加对象行为.Go语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式.使用匿名组合,在装饰器中不必显式定义转调原对象方法. 设计模式 装饰器模式 装饰器模式主要 ...

最新文章

  1. 一分钟学会spring注解之@Scope注解
  2. 深度学习概述:当你没有方向时的加油站
  3. 学习笔记之与 30 家公司过招,得到了这章面试心法
  4. 温州大学《机器学习》课程课件(十、聚类)
  5. SAP CDS entity 中使用 @readonly 进行访问控制
  6. 在FC中如何获取fcdot文件
  7. 夯实基础项目工程之图论——Uncle Bogdan and Country Happiness,Graph Coloring,How Many Paths?,Array Differentiation
  8. Xcode 4-PBXcp error修复-No such file or directory
  9. ruby rails
  10. 百融大数据自助查询_百融大数据
  11. 怎么用计算机向手机上传照片,华为手机怎么传照片到电脑上去?华为手机往电脑传照片方法...
  12. 使用docker搭建nodebb论坛
  13. 【水滴云|热点】个人信息安全有法可依,海量数据用IPFS存储
  14. 【MarkDown使用技巧】轻松搞定MarkDown
  15. 【POj1305】毕达哥拉斯三元组
  16. loadrunner的吞吐量和吞吐率
  17. bios设置raid启动模式Linux,手把手教你做raid,各主板raid开启设置方法
  18. box-sizing: border-box;失效问题
  19. 变更后的微软365服务器,从Exchange2010迁移Office365混合部署(5)
  20. 币圈拉盘砸盘内幕:30分钟,庄家就彻底把你套牢

热门文章

  1. 国企安逸?我笑了……
  2. MBR30300VPT-ASEMI肖特基二极管MBR30300VPT
  3. 【雕爷学编程】Arduino动手做(69)---GY-30环境光传感器
  4. flash player官网地址 建议不要下载flash.cn的
  5. Java 基础 - List 遍历时为什么不能通过 for 循环进行删除,而使用 Iterator 可以 ?
  6. 银行卡号码校验算法(Luhn算法,又叫模10算法)
  7. 银行卡号码的校验规则(Luhn算法/模10算法)
  8. 牛客网ACM模式 JsV8和Java输入输出练习
  9. D3D9学习笔记之颜色
  10. 云原生与业务可观测性