`
dongchen916
  • 浏览: 40916 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类

装饰器(Decorator)模式

阅读更多
(转的 写的不错)


装饰器(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中装饰器模式的实现可以通过下面的源代码分析从而了解细节。


分享到:
评论

相关推荐

    装饰器模式(Decorator Pattern) 1. 装饰器模式简介 1.1 问题引入 1.2 装饰器模式的解决方案 2. 装饰器模式的定义和原理 2.1 定义 2.2 基本原理 2.3 装饰器模式

    装饰器模式(Decorator Pattern) 1. 装饰器模式简介 1.1 问题引入 1.2 装饰器模式的解决方案 2. 装饰器模式的定义和原理 2.1 定义 2.2 基本原理 2.3 装饰器模式的特点 3. 装饰器模式的UML类图和基本结构 3.1 UML...

    装饰器模式[Decorator]

    装饰器模式(Decorator)是一种设计模式,它允许在运行时向对象添加新的行为或责任,而无需修改对象的源代码。这种模式属于结构型模式,是面向对象设计中的一种非常实用的技术。 装饰器模式的核心思想是通过将一个...

    WPF 装饰器Decorator 和 Adorner综合实例

    装饰器模式是一种设计模式,它允许我们在不修改原有对象的基础上,为对象添加新的行为或属性。在WPF中,Decorator类通常用于改变控件的外观,如添加边框、背景等。以标题中提到的"自定义边框"为例,我们可以创建一个...

    设计模式之装饰模式(Decorator Pattern)

    4. **具体装饰器(Concrete Decorator)**:实现了装饰器接口,为组件增加新的行为或属性。每个具体装饰器都对应一种特定的扩展功能。 装饰模式的优点在于其灵活性,它可以独立于原始对象进行扩展,不需要修改原有...

    PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

    装饰器模式(Decorator Pattern)是一种结构型设计模式,主要用于在运行时动态地给对象添加新的职责或行为,而不必改变现有对象的类定义。在面向对象编程中,装饰器模式提供了一种相对于继承更加灵活的方式来增强或...

    基于JavaScript装饰器Decorator实现的通信库

    在"基于JavaScript装饰器Decorator实现的通信库"——eventbus-cjs中,装饰器被用来简化组件间通信,特别是对于Vue和React这样的前端框架。EventBus是一种常见的实现组件间通信的方式,它可以作为一个轻量级的消息...

    装饰器decorator_python_

    在`decorator.py`中,可能会包含更复杂的装饰器实现,比如带有参数的装饰器或者类装饰器。带参数的装饰器允许我们在应用装饰器时传入一些配置,以适应不同场景的需求。类装饰器则通过定义一个类来实现,这种方式更加...

    C#设计模式之Decorator 装饰模式

    4. 具体装饰器(Concrete Decorator):实现了装饰器的抽象方法,并添加新的行为或职责。 在"C#面向对象设计模式纵横谈(10):(结构型模式) Decorator 装饰模式.pdf"中,你可能会学习到如何定义这些角色以及它们之间...

    java Decorator装饰模式例子

    `Decorator`是装饰器,持有`DecoratorComponent`的引用,并实现了相同的操作方法。`AdditionalFeatureA`和`AdditionalFeatureB`是具体装饰器,分别添加了功能A和功能B。 通过以下代码,我们可以创建一个装饰器链并...

    c++-设计模式之装饰模式(Decorator)

    具体装饰器(Concrete Decorator):扩展装饰器的功能,提供额外的行为 总结 装饰模式通过在运行时动态地为对象添加功能,增强了系统的灵活性和可扩展性。它允许在不修改现有代码的情况下添加新的行为,非常适合于...

    java Decorator模式的简单例子

    首先,Decorator模式的核心组件包括抽象组件(Component)、具体组件(Concrete Component)、装饰器(Decorator)以及具体装饰器(Concrete Decorator)。抽象组件定义了对象的基本接口,所有组件都必须实现这个...

    设计模式C++学习之装饰模式(Decorator)

    4. 具体装饰(Concrete Decorator):装饰器的实现,添加了具体组件的特定增强功能。每个具体装饰类可以添加不同的行为,从而提供多种装饰方式。 举个例子,假设我们有一个`Coffee`类,表示基础的咖啡。`Coffee`...

    Decorator模式的应用

    在实际的IT开发中,Decorator模式有很多应用场景,比如在数据库连接池(如C3P0、DBCP)中,通过装饰器可以添加连接验证、池化管理等高级特性。在网络通信中,可以使用装饰器来实现数据的压缩、加密等功能,而不改变...

    Decorator模式

    Decorator模式的核心思想是通过将对象包装在一个装饰器对象中来增加新功能。装饰器和被装饰的对象拥有相同的接口,使得客户端代码可以透明地使用装饰后的对象,而不需要知道对象已经被增强了。这种设计使得代码更加...

    设计模式-装饰器模式

    装饰器模式是一种结构型设计模式,它允许在不修改对象本身的情况下动态地为对象添加新的行为或职责。这种模式在软件工程中广泛应用,特别是在需要扩展已有功能而不影响原有代码结构时。在iOS开发中,装饰器模式同样...

    C++ Decorator模式

    Decorator模式是设计模式中的一种结构型模式,它允许在运行时动态地给对象添加新的行为或职责,而不会破坏封装性。这种模式的核心思想是通过装饰类包装原对象,实现对原对象功能的扩展,同时保持与原接口的一致性。 ...

    结构型模式之装饰模式(Decorator)

    4. **具体装饰器(Concrete Decorator)**:实现了装饰器的抽象方法,提供了具体的增强功能。每个具体装饰器都为原始组件增加特定的职责。 装饰模式的工作流程如下: 1. 客户端首先创建一个原始组件对象。 2. 然后...

    python使用装饰器(Decorator)的方式实现单例模式

    demo python使用装饰器(Decorator)的方式实现单例模式 functools.wraps 则可以将原函数对象的指定属性复制给包装函数对象, 默认有 __module__、__name__、__doc__,或者通过参数选择

Global site tag (gtag.js) - Google Analytics