前言

   在食品工业中的装饰图案具有比较广泛的应用,大多数的两个图案和在网上的例子饮食相关的,一旦被称为电影的手表,点咖啡要加糖要加奶昔要加这加那的时候。感觉好有派~好高大上啊~。为啥我在小卖部都是“来瓶汽水”就没话说了呢~,难道是我不会“装”?
 

官方定义

   动态的给一个对象加入一些职责,就添加功能来说。该模式比生成子类更为灵活——GOF
 
   Decorator模式是一种相对简单的对象结构性模式,动态和对象是个相应的关系。正如静态和类这种相应关系,编译时可以决定的特质是静态特质,动态则表示在执行时进行操作,传统情况下使用的继承是静态的给类加入职责。动态的给对象加入职责。则是装饰者模式所要完毕的事。
   给类和给对象加入职责有什么不同呢,前者的灵活性要差。假设须要加入的职责非常多,前者须要为每种情况都定义一个固定类,这里的每种情况指的是职责的排列组合,假如我要为一个原始类加入5个职责。则会出现5的阶乘种情况,不不过类爆炸带来的繁琐,执行时对职责的改动是随意的,这就使得编译时确定的类又要在执行时频繁改变,而直接往对象中加入职责则使的结构能够灵活多变,相同的情况,我只须要额外添加5个修饰类就能够完美解决类爆炸及执行时改变的情况,这就是上述装饰者模式定义所要表达的详细含义。
 

场景

       产生类爆炸的原因非常多,并非每种情况都能够用装饰者模式来解决,该模式应用的典型场景需满足下面三点
 1 原始组件(被装饰者)和装饰者符合逻辑上的修饰关系时
       这个比較好理解,HeadFirst设计模式举了个为咖啡加入不同口味的配料,不同口味的配料相应一个装饰者,通常来讲。代码结构要反映出逻辑关系。不管是哪种口味的配料,他对咖啡对象确实产生了修饰关系,网上关于装饰者模式的举例大部分是关于饮食类,这是有道理的,不同口味或者配料相应不同的装饰者。这样是符合现实世界的逻辑关系的。
 2 装饰者之间是能够独立存在的
   还是继续咖啡的样例,每种口味之间都是独立关系,互相之间没有依赖关系。这个依据客户的需求。有人喜欢摩卡口味。有人喜欢奶油口味,这两种修饰者能够独立修饰咖啡的。当然有人喜欢摩卡奶油咖啡,这个不过修饰顺序的问题,不影响两种口味的独立性。假设修饰者之间不满足这样的独立性。使用装饰者模式是不合理得。
 3 当无法确定原始组件被装饰的方式和时机时
   一杯咖啡,依据客户需求的不同,加入不同口味的配料。客户会喜欢那种口味或者哪几种,事先是不确定的,咖啡店也不须要事先确定。他们会把不同口味的配料放在不同的机器中,随要随取。灵活应对需求,假设须要新口味。加入一个新机器就能够,扩展灵活,至少这些咖啡店的老板。都是懂装饰者模式的。

 

举例

          装饰者模式的经典样例非常多,没有必要反复造轮子,就接着咖啡这个样例来说吧。

装饰者模式最核心的部分,就是从下图開始

 
   一杯咖啡代表被装饰的组件component,咖啡与被辅料之间有两种关系,组合与继承。IS-A和Has-A的关系,HAS-A比較好理解,通过组合一个被装饰对象。能够使用被装饰对象的操作并进行装饰,IS-A则和通常情况有差别。辅料(Decorator)本质上并非咖啡。所以这里的继承关系并非类继承。而是接口(协议)继承。其含义就是经过辅料修饰后的咖啡仍然具有咖啡本身所含有的接口特性,也就是描写叙述和价格,不能把这个继承关系理解成调料也是一种咖啡,之所以使用继承的原因是能够方便灵活的在执行时动态加入职责,后面的測试例结果能够看出这一点。

   和上述UML图对于的源代码例如以下
   Cafe.java
package com.klpchan.example.cafe;

public abstract class Cafe {

	public String getDescription() {
return description;
} public abstract float getPrice(); String description = "This is Cafe";
}
   Decorator.java
package com.klpchan.example.cafe;

public abstract class Decorator extends Cafe{

	public Decorator(Cafe _cafe) {
// TODO Auto-generated constructor stub
this.cafe = _cafe;
} @Override
public String getDescription() {
// TODO Auto-generated method stub
return cafe.getDescription();
} @Override
public float getPrice() {
// TODO Auto-generated method stub
return cafe.getPrice();
} Cafe cafe;
}
   咖啡作为被修饰者。含有非常多详细的子类,顾客选择时首先要选择详细的某种咖啡(详细component)。然后在为其选择口味(详细Decorator),本例选择脱脂(DECAF)和意式(Espresso)两种咖啡,第二图UML图诞生了

 
 
   两种详细的咖啡是被修饰的详细类,各自己定义了咖啡的价格和说明,源代码例如以下
 DeCaf.java
package com.klpchan.example.cafe;

public class DeCaf extends Cafe{

	public DeCaf() {
// TODO Auto-generated constructor stub
description = "This is DECAF cofe";
} @Override
public float getPrice() {
// TODO Auto-generated method stub
return Constants.CAFE_DECAF_PRICE;
}
}
Espresso.java
package com.klpchan.example.cafe;

public class Espresso extends Cafe{

	public Espresso() {
// TODO Auto-generated constructor stub
description = "This is Espresso " ;
} @Override
public float getPrice() {
// TODO Auto-generated method stub
return Constants.CAFE_ESPRESSO_PRICE;
}
}
 
         选择好咖啡后,用户開始选择口味配料(Decorator),这就是装饰模式的核心。本比如果有三种配料
   摩卡(Mocha)、奶油(Milk)和巧克力(Chocolate),每种配料都有各自的价格,价格表详细例如以下
package com.klpchan.example.cafe;

public class Constants {
//脱脂和意式两种咖啡的基本价格
public static final float CAFE_DECAF_PRICE = 8;
public static final float CAFE_ESPRESSO_PRICE = 9; //摩卡、牛奶、巧克力三种口味调料的价格
public static final float DECORATOR_MOCHA_PRICE = 0.5f;
public static final float DECORATOR_MILK_PRICE = 0.4f;
public static final float DECORATOR_CHOCOLATE_PRICE = 0.8f;
}
        通过加入详细的配料类来修饰咖啡,UML图例如以下
 

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva2xwY2hhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

 

加入了三个详细的配料类摩卡、奶油和巧克力,源代码例如以下

MochaDecorator.java
package com.klpchan.example.cafe;

public class MochaDecorator extends Decorator{

	public MochaDecorator(Cafe _cafe) {
super(_cafe);
// TODO Auto-generated constructor stub
} @Override
public String getDescription() {
// TODO Auto-generated method stub
return super.getDescription() + " add mocha ";
} @Override
public float getPrice() {
// TODO Auto-generated method stub
return super.getPrice() + Constants.DECORATOR_MOCHA_PRICE;
}
}
 
MilkDecorator.java
package com.klpchan.example.cafe;

public class MilkDecorator extends Decorator{

	public MilkDecorator(Cafe _cafe) {
super(_cafe);
// TODO Auto-generated constructor stub
} @Override
public String getDescription() {
// TODO Auto-generated method stub
return super.getDescription() + " add milk ";
} @Override
public float getPrice() {
// TODO Auto-generated method stub
return super.getPrice() + Constants.DECORATOR_MILK_PRICE;
}
}
ChocolateDecorator.java
package com.klpchan.example.cafe;

public class ChocolateDecorator extends Decorator{

	public ChocolateDecorator(Cafe _cafe) {
super(_cafe);
// TODO Auto-generated constructor stub
} @Override
public String getDescription() {
// TODO Auto-generated method stub
return super.getDescription() + " add chocolate ";
} @Override
public float getPrice() {
// TODO Auto-generated method stub
return super.getPrice() + Constants.DECORATOR_CHOCOLATE_PRICE;
}
}
         在每个详细的修饰类中,新方法使用了被修饰对象本身的方法并加上了新的特性,本例中是加上了说明和配料价格,新方法能够用在被修饰对象方法的前或者后,能够动态改变被修饰对象的状态和操作。被修饰对象(咖啡)并不须要知道修饰者(调料)究竟做了那些操作,这些都是透明的。

 
   在客户文件写了个測试比例如以下
  
		Cafe cafe = new DeCaf();
Cafe mochaChocCafe = new MochaDecorator(new ChocolateDecorator(cafe));
System.out.println(mochaChocCafe.getDescription() + " Price is " + mochaChocCafe.getPrice()); Cafe cafe2 = new Espresso();
Cafe milkChocMochaCafe = new MochaDecorator(new ChocolateDecorator(new MilkDecorator(cafe2)));
System.out.println(milkChocMochaCafe.getDescription() + " Price is " + milkChocMochaCafe.getPrice());
 
        能够看出对于一个选定的咖啡类,用户能够自由组合其装饰者,这就是较继承灵活的体现,执行结果例如以下
This is DECAF cofe add chocolate  add mocha  Price is 9.3
This is Espresso  add milk  add chocolate  add mocha  Price is 10.7
         对于上述源代码中的价格表及測试源代码,能够看出两个订单的说明和价格均被正确改动完毕。

 

适用性

  1 以动态透明的方式加入职责,动态前文已提及,透明是指被修饰者不依赖与修饰者,详细咖啡不依赖与调料,以脱脂咖啡为例,该类不须要关系是摩卡还是巧克力来修饰自己。更不关心这些修饰类怎样来修饰自己。代码结构中能够表现为咖啡类并不存在修饰类的引用。

      2 处理能够撤销的职责。相当于上述过程的逆向project。将职责模块化,能够在执行时动态添加删除。
  3 当无法使用继承来加入职责时。除了前文所述的因为职责过多引起的类爆炸。也可能是因为类定义被隐藏使得无法生成子类。
   

结构

   
   这个和上述UML图的结构基本吻合。
   Component 被修饰的组件抽象。本例中表示咖啡类,正如前文所述,装饰者和被装饰者须要同一时候继承这个抽象类,这样能够动态的为对象加入职责。
   ConcreteComponent 详细的被修饰对象。本例中有脱脂咖啡和意式咖啡,详细组件不关心装饰类怎样装饰他们。
   Decorator 装饰者抽象,本例中的调料基类,和组件是继承与组合的关系。装饰基类原封不动的调用被装饰者的操作。供详细修饰者重写。
   ConcreteDecoratorA/B 详细装饰者。本例中的摩卡、牛奶、巧克力装饰类。能够改动被修饰者状态和行为。
 

效果

      1 比静态继承更加灵活,前文已述。
  2 避免在较高层次有较多特征,查看UML结构,组件和装饰者都有着较为简单的特征和操作。在使用该模式时。GOF建议为简单的类逐步加入功能而不是直接修饰一个复杂的类。由于逐步加入的功能能够组合出新的复杂功能。而直接扩展复杂的类easy暴露非常多与职责无关的细节,应该尽量保持组件的简洁性,组件应该做的是定义接口一类的工作,本例中的组件Cafe,只定义了主要的说明操作,由于装饰者也须要继承组件,在组件中定义过多且与职责没用的功能,会添加装饰者类的复杂程度,这段内容我理解的也不透彻,欢迎分享讨论~
  3 Decorator与Component不一样。前文已述。被装饰的组件与装饰者的继承关系为接口继承,并不是类继承,这样做的目的是有利于多次修饰。

我们能够使用一个或多个装饰者来装饰一个对象,装饰后的对象仍然是component对象。

  4 有很多小对象,这个能够參阅本文上述的測试程序,不同的装饰组合会产生的不同的对象,这个是装饰者模式的缺点。
  5 为了解决扩展职责所带来的类爆炸和灵活性问题,该模式使用组合而非继承的格式,继承所产生的大量子类难以维护。更无法应对职责改变所引起的扩展性问题。

 
 

和其他模式的关系

     适配器模式是改变一个对象的接口类型,使他成为可以满足与其他接口相匹配的要求。而装饰者模式是为对象动态透明的加入职责。
   装饰者模式改变外壳,策略模式改变内核。事实上对象的外壳和内核是个相对的观点,类似于本例中的咖啡,咖啡的外壳改变就是前文所说口味的变化,口味变化并不影响咖啡的本质,意式咖啡怎样加糖也成为不了脱脂咖啡。而假设把意式咖啡在制作过程中的一些工艺替换了,则改变了内核,制作工艺的不同是意式咖啡和其他类型咖啡本质的差别,此时使用策略模式比較合理。这就是两者的差别。
 
 

应用场景

        .NET框架中的装饰者模式应用,结构清晰不赘述了。
   

收尾

          装饰者模式是较易理解的经常使用结构性模式,通过组合而非继承来解决原始类动态加入职责所引起的问题,本文偷个懒,直接改写了HEAD First设计模式的样例,此类样例解释的比較形象。对于一些较为晦涩的理论给出了自己的理解。模式这样的东西每一个人都会有不同的理解,细节上会有差异,欢迎分享交流,共同进步~
 
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
注:欢迎分享。转载请声明~~
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

版权声明:本文博主原创文章,博客,未经同意不得转载。

举例说, Decorator模式(Decorator Pattern)的更多相关文章

  1. 浅谈设计模式--装饰者模式(Decorator Pattern)

    挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...

  2. C#设计模式之装饰者模式(Decorator Pattern)

    1.概述 装饰者模式,英文名叫做Decorator Pattern.装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 2 ...

  3. 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法

    装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...

  4. 设计模式 - 装饰者模式(Decorator Pattern) 具体解释

    装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...

  5. 设计模式学习--装饰者模式(Decorator Pattern)

    概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...

  6. 23种设计模式之装饰器模式(Decorator Pattern)

    装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...

  7. python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

  8. Java设计模式(7)装饰模式(Decorator模式)

    Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这 ...

  9. 来杯咖啡-装饰者模式(Decorator)

    前言 上篇[观察者模式]发布已经近一个月了,个人感觉反应并不太理想,因为大家响应都不是很积极,不知是文章那里写得有问题,而且也没有人提出过有价值的改进建议,多少感觉有些失望L!因为工作繁忙,所以不可能 ...

随机推荐

  1. Selenium: 空指针error

    Error 类型:空指针 可能原因一: 只是引用了该类,但是没有对该类进行实例化(即没有New 一下),即没有给该类分配内存,所以导致空指针: 类调用前注意要实例化,否则会导致空指针错误. 首先声明D ...

  2. mysql left join,right join,inner join用法分析

    下面是例子分析表A记录如下: aID        aNum 1           a20050111 2           a20050112 3           a20050113 4   ...

  3. nyoj 130 同样的雪花 【哈希】

    同样的雪花 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描写叙述 You may have heard that no two snowflakes are alike. ...

  4. hdu1028(整数划分问题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1028 整数划分问题 整数划分 --- 一个老生长谈的问题: 描述 整数划分是一个经典的问题.请写一个程 ...

  5. hdu3033(变形分组背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3033 题意:Iserlohn要买鞋,有k种牌子,每种牌子至少买一双鞋子.每双鞋子有标价跟实际价值.求用 ...

  6. BaiduMap_SDK_DEMO_3.0.0_for_Xamarin.Android_by_imknown

    2.4.2 已稳定, 同一时候已经放置到分支/Release 2.4.2了. 3.0.0 已开发完毕, 可是不推荐大家用于项目中, 请观望或者自己进一步调试. 个人感觉尽管3.0.0简化了开发, 可是 ...

  7. 理解Javascript的动态语言特性

    原文:理解Javascript的动态语言特性 理解Javascript的动态语言特性 Javascript是一种解释性语言,而并非编译性,它不能编译成二进制文件. 理解动态执行与闭包的概念 动态执行: ...

  8. Java集群--大型网站是怎样解决多用户高并发访问的

    时间过得真快,再次登录博客园来写博,才发现距离上次的写博时间已经过去了一个月了,虽然是因为自己找了实习,但这也说明自己对时间的掌控能力还是没那么的强,哈哈,看来还需不断的努力啊!(这里得特别说明一下本 ...

  9. hdu1507--二分图最大匹配

    题意:你大爷.哦不! 你大叔继承了一块地什么的都是废话..,这里说说题意,和怎么建图. 题意:这里有一块N*M的地,可是有 K 个地方.是池塘,然后输入K行(x,y),OK,如今能够出售的地必须是 1 ...

  10. ok6410 u-boot-2012.04.01移植七完善u-boot移植(u-boot移植结束)

    继ok6410 u-boot-2012.04.01移植六后,开发板已支持MLC NAND.DM9000等.但还需要完善比如环境变量.mtdpart分区.裁剪.制作补丁等.下面的工作就是完善移植的u-b ...