`

设计模式之 Decorator - 装饰模式

阅读更多

装饰模式,The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

 

       让我们来理解这句话。

 

我们系统中设计了这个类。 <!--[if !vml]--><!--[endif]-->

现在,在系统的一个地方需要一个能够报警的Door,你来怎么做呢?你或许写一个Door的子类AlarmDoor,在里面添加一个子类独有的方法alarm()。嗯,那在使用警报门的地方你必须让客户(使用者)知道使用的是警报门,不然无法使用这个独有的方法。而且,这个还违反了Liskov替换原则。

也许你要说,那就把这个方法添加到Door里面,这样不就统一了?但是这样所有的门都必须有警报,至少是个哑巴警报。而当你的系统仅仅在一两个地方使用了警报门,这明显是不合理的——虽然可以使用缺省适配器来弥补一下。

       这时候,你可以考虑采用装饰模式来给门动态的添加些额外的功能。

       下面我们来看看装饰模式的组成,不要急着去解决上面的问题,到了下面自然就明白了!

 

Component
原有类的接口
ConcreteComponent
功能实现类。Component的具体实现类
Decorator
饰抽象类。与ConcreteComponent一样都继承了Component接口,但实现的方式与ConcreteComponent有区别。 ConcreteComponent通过单纯继承的方式来实现;而Decorator则通过对Component对象的封装与动作委让方式来实现。
下面是一个典型的Decorator的代码为:
清单1
public class Decorator implement Component {
    Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    public void doSomething() {
        ...
        this.component.doSomething();
        ...
    }
}
ConcreteDecoratorA ConcreteDecoratorB
具体的装饰类。Decorator的具体实现类

 

下面是类图


下面看一个更具体的例子:

       Central Perk的名字因为《老友记》而享誉全球,他们的分店几乎开遍世界各地。他们发展的实在是太快了,所以他们此时正在急于实现一套由计算机管理的自动化记账系统。

对于一家咖啡店来说,都有那些变化点呢?调味品的品种和价格会变吗?咖啡的品种和价格会变吗?咖啡和调味品的组合方式会变吗?YES! 对于一家咖啡店来说,这些方面肯定会经常发生改变的!那么,当这些改变发生的时候,我们的记账系统要如何应对呢? 如果调味品发生改变,那么我们只能从代码的层次重新调整Beverage基类,这太糟糕了;如果咖啡发生改变,我们可以增加或删除一个子类即可,这个似乎还可以忍受;那么咖啡和调味品的组合方式发生改变呢?如果顾客点了一杯纯黑咖啡外加两份砂糖和一份巧克力,或者顾客点了一杯脱咖啡因咖啡(Decaf)外加三份炼乳和一份砂糖呢?

 

       那么回到装饰者模式,跟前面介绍过的模式一样,装饰者同样是一个很简单的模式,特别是画出类图和顺序图之后,一切都很清楚明了。这里只有一个地方需要特殊强调一下:Decorator是装饰者模式里非常特殊的一个类,它既继承于ComponentIS A关系】,又维护一个指向Component实例的引用【HAS A关系】,换个角度来说,DecoratorComponent之间,既有动态组合关系又有静态继承关系,WHY? 这里为什么要这么来设计?上面我们说过,组合的好处是可以在运行时给对象增加职责,DecoratorHAS AComponent的目的是让ConcreteDecorator可以在运行时动态给ConcreteComponent增加职责,这一点相对来说还比较好理解;那么Decorator继承于Component的目的是什么?在这里,继承的目的只有一个,那就是可以统一装饰者和被装饰者的接口,换个角度来说,不管是ConcretComponent还是ConcreteDecorator,它们都是 Component,用户代码可以把它们统一看作Component来处理,这样带来的更深一层的好处就是,装饰者对象对被装饰者对象的功能职责扩展对用户代码来说是完全透明的,因为用户代码引用的都是Component,所以就不会因为被装饰者对象在被装饰后,引用它的用户代码发生错误,实际上不会有任何影响,因为装饰前后,用户代码引用的都是Component类型的对象,这真是太完美了!装饰者模式通过继承实现统一了装饰者和被装饰者的接口,通过组合获得了在运行时动态扩展被装饰者对象的能力。

我们再举个生活中的例子,俗话说“人在衣着马在鞍”,把这就话用装饰者模式的语境翻译一下,“人通过漂亮的衣服装饰后,男人变帅了,女人变漂亮了;”。对应上面的类图,这里人对应于ConcreteComponent,而漂亮衣服则对应于ConcreteDecorator;换个角度来说,人和漂亮衣服组合在一起【HAS A】,有了帅哥或美女,但是他们还是人【IS A】,还要做人该做的事情,但是可能会对异性更有吸引力了(扩展功能)    

从装饰者模式的角度来考虑问题,咖啡和调味品的关系应该 是:咖啡是被装饰对象而调味品是装饰者,咖啡和调味品可以任意组合,但是不管怎么组合,咖啡还是咖啡!原来这么简单啊!

具体看下面的类图:

 



       如图所示,Beverage还是所有饮料的基类,它对应于装饰者模式类图里的Component,是所有被装饰对象的基类;HouseBlend, DarkRoast, Espresso, Decaf是具体的饮料(咖啡)种类,对应于前面的ConcreteComponent,即是具体的被装饰对象;CondimentDecorator对应于前面的Decorator,是装饰者的抽象类;而MilkMochaSoyWhip则都是具体的调味品,对于前面的ConcreteDecorator,也就是具体的装饰者。下面我们通过具体的代码再进一步理解一下基于装饰者模式的记账系统的实现。  


Beverage还是所有饮料的基类,它对应于装饰者模式类图里的Component,是所有被装饰对象的基类

 


package zieckey.designpatterns.study.decorator;

/**
 *
 * 设计模式之Decorator - 装饰模式
 *
 * Beverage 是所有饮料的基类,它对应于装饰者模式类图里的Component
 *
 * @author zieckey
 *
 */

public abstract class Beverage {

    public String getDescription()
    {
        return "Unknown Beverage";
    }
    
    public abstract double cost();
}


HouseBlend, DarkRoast, Espresso, Decaf是具体的饮料(咖啡)种类,对应于前面的ConcreteComponent,即是具体的被装饰对象;

package zieckey.designpatterns.study.decorator;

/**
 * DarkRoast咖啡,具体的Component
 * @author zieckey
 *
 */

public class DarkRoast extends Beverage {

    @Override
    public double cost() {
        // TODO Auto-generated method stub

        return 0.88;
    }

    public String getDescription() {
        return "DarkRoast";
    }    
}


package zieckey.designpatterns.study.decorator;

/**
 * Expresso咖啡,具体的Component
 * @author zieckey
 *
 */

public class Expresso extends Beverage {

    @Override
    public double cost() {
        // TODO Auto-generated method stub

        return 0.95;
    }

    public String getDescription() {
        return "Expresso";
    }
    
    
}


CondimentDecorator对应于前面的Decorator,是装饰者的抽象类

package zieckey.designpatterns.study.decorator;

/**
 * 装饰抽象类
 * @author zieckey
 *
 */

public abstract class CondimentDecorator extends Beverage{
    Beverage beverage;
    
    public CondimentDecorator( Beverage beverage )
    {
        this.beverage = beverage;
    }
    
    public double cost()
    {
        return beverage.cost();
    }
    
}


MilkMochaSoyWhip则都是具体的调味品,对于前面的ConcreteDecorator,也就是具体的装饰者


package zieckey.designpatterns.study.decorator;

//mocha咖啡,具体的Decorator

public class MochaDecorator extends CondimentDecorator {
    public MochaDecorator(Beverage beverage) {
        super(beverage);
    }

    public String getDescription() {
        return beverage.getDescription() + "+Mocha";
    }

    public double cost() {
        return 0.20 + beverage.cost();
    }
}


package zieckey.designpatterns.study.decorator;

/**
 * soy咖啡,具体的Decorator
 * @author zieckey
 *
 */

public class SoyDecorator extends CondimentDecorator {

    public SoyDecorator(Beverage beverage) {
        super(beverage);
    }

    public String getDescription() {
        return beverage.getDescription() + "+Soy";
    }

    public double cost() {
        return 0.15 + beverage.cost();
    }
}


好了。下面给出测试代码:

package zieckey.designpatterns.study.decorator;

public class ClientTest {

    /**
     * @param args
     */

    public static void main(String[] args) {
        Beverage expresso = new Expresso();
        CondimentDecorator soyDecorator1 = new SoyDecorator( expresso );
        CondimentDecorator mochaDecorator1 = new MochaDecorator( soyDecorator1 );
        
        System.out.println( mochaDecorator1.getDescription() + ", costs, " + mochaDecorator1.cost() );
        
        Beverage darkRoast = new DarkRoast();
        CondimentDecorator soyDecorator2 = new SoyDecorator( darkRoast );
        CondimentDecorator mochaDecorator2 = new MochaDecorator( soyDecorator2 );
        System.out.println( mochaDecorator2.getDescription() + ", costs, " + mochaDecorator2.cost() );
        
    }

}


输出:

Expresso+Soy+Mocha, costs, 1.2999999999999998
DarkRoast+Soy+Mocha, costs, 1.23


至此应该讲清楚了什么是装饰模式了吧。

说明:
本文是对以下三个连接的博文进行整理得到的,相信看一下三篇博文会有更大的收获。

参考:

http://blog.csdn.net/ai92/archive/2005/03/01/307046.aspx

http://www.cnblogs.com/justinw/archive/2007/06/11/779356.html

http://www.lifevv.com/sysdesign/doc/20071116210104656.html

分享到:
评论

相关推荐

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

    装饰模式(Decorator Pattern)是设计模式中的一种结构型模式,它在不改变原有对象的基础上,通过添加额外的职责来扩展对象的功能。在C#中,装饰模式尤其适用于那些需要动态地增加或减少对象功能的情况,避免了使用...

    Java设计模式之-Decorator装饰模式

    Decorator模式,也称为装饰模式,是设计模式中的一个重要组成部分,它在不改变原有对象接口的前提下,动态地给对象添加新的功能,从而扩展了对象的能力。这篇博客()将深入探讨这个模式的细节。 装饰模式的核心...

    设计模式精解-GoF-23种设计模式解析--附C++源代码

    - 装饰模式(Decorator):动态地给一个对象添加一些额外的职责。 - 享元模式(Flyweight):使用共享对象,有效地支持大量细粒度的对象。 3. **行为型模式**:这类模式关注对象之间的责任分配。 - 责任链模式...

    设计模式--装饰者模式java例子

    装饰者模式是软件设计模式中的一种结构型模式,它的主要目的是动态地给对象添加新的功能,而无需修改原有代码。在Java中,装饰者模式通常通过继承和组合来实现,它提供了一种比继承更灵活的方式来扩展对象的功能。...

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

    装饰模式(Decorator Pattern)是一种结构型设计模式,它在不改变原有对象的基础上,通过包裹一个对象并为其添加新的行为或责任,实现对对象功能的扩展。这种模式在软件开发中非常常见,尤其当需要在运行时动态改变...

    设计模式---装饰者模式

    装饰者模式是一种结构型设计模式,它允许在运行时向对象添加新的行为或职责,而无需修改对象的源代码或创建子类。这种模式的核心思想是通过将对象封装在一个装饰器对象内,来扩展对象的功能,同时保持原有接口的不变...

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

    装饰模式(Decorator Pattern)是一种结构型设计模式,允许在不改变对象接口的情况下,动态地为对象添加额外的职责或功能。装饰模式通常用于需要扩展对象功能而又不希望使用子类化的场景。 装饰模式的组成 组件接口...

    设计模式之 Decorator模式和代码实现

    【Decorator模式】是一种设计模式,它允许在运行时动态地给对象添加新的责任或功能,而不必通过子类化的方式。在上述的奇幻RPG游戏中,Decorator模式被用来实现武器的锻造过程,尤其是武器镶嵌宝石的功能。这个过程...

    通过C#实现设计模式-装饰模式(DecoratorPattern).rar

    装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你向一个现有的对象添加新的功能,同时又不改变其结构。装饰模式通过创建一个装饰类,该类包装了原始类的实例,并在调用原始类方法之前或之后添加额外的...

    java设计模式---诙谐易懂版

    代理模式(Proxy Pattern)、单例模式(Singleton Pattern)、工厂方法...装饰模式(Decorator Pattern)、迭代器模式(Iterator Pattern)、组合模式(Composite Pattern)、观察者模式(Observer Pattern)、责任链...

    decorator-4.1.2.tar.gz

    装饰器(Decorator)在Python编程语言中是一种强大的设计模式,它允许我们动态地修改或增强函数、类或对象的行为。`decorator-4.1.2.tar.gz` 是一个特定版本(4.1.2)的装饰器库的归档文件,这个库专门用于支持...

    HeadFirst 设计模式学习笔记3--装饰模式 Demo

    装饰模式是一种结构型设计模式,它允许在运行时向对象添加新的行为或责任,而无需修改对象的源代码。这种模式通常用于保持对象的原始类结构不变,同时增强其功能。HeadFirst 设计模式系列书籍以其生动有趣的插图和...

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

    装饰模式(Decorator)是软件设计领域中一种非常实用的结构型设计模式,它允许我们向一个对象添加新的行为或责任,而无需修改该对象的源代码。在C++编程语言中,装饰模式常用于动态地扩展类的功能,使得类的行为在...

    设计模式专题之(七)装饰模式---设计模式装饰模式示例代码(python--c++)

    装饰模式是一种结构型设计模式,它允许在运行时动态地给对象添加新的行为或职责,同时保持对象的接口不变。这种模式使得我们可以在不修改原有代码的基础上,通过组合不同的装饰来扩展对象的功能,实现了代码的高可...

    23种设计模式--装饰模式

    装饰模式是一种结构型设计模式,它是面向对象设计中用来动态添加或修改对象功能的一种方法。在软件工程中,装饰模式允许我们向一个现有的对象添加新的行为或职责,同时又不改变其原有的结构,从而实现对类的功能扩展...

    设计模式之Decorator

    《设计模式之Decorator》 设计模式是软件工程中的一种最佳实践,它是在特定场景下解决常见问题的经验总结。Decorator模式是一种结构型设计模式,它的主要作用是为对象添加额外的功能,而无需修改对象的源代码。...

    c++设计模式-结构型模式-装饰器模式

    c++设计模式-结构型模式-装饰器模式;QT工程;c++简单源码; 装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

    设计模式 - 装饰模式(C++实例)

    装饰模式是一种结构型设计模式,它允许在运行时向对象添加新的行为或责任,而无需修改对象的源代码。这种模式在软件工程中非常常见,因为它提供了灵活性,使得我们可以独立于对象的组合来扩展功能。 在C++中,装饰...

    C++设计模式--基于Qt4开源跨平台开发框架

    《C++设计模式--基于Qt4开源跨平台开发框架》一书主要探讨了如何在C++编程中利用设计模式,并结合Qt4框架进行高效的跨平台应用开发。设计模式是软件工程中的重要概念,它们是经过时间和实践验证的解决特定问题的模板...

    26种设计模式-----pdf

    设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的模板。这个压缩包文件名为"26种设计模式",其中可能详细介绍了软件开发中的26个核心设计模式。这些模式是经过时间检验、被广泛接受并反复使用...

Global site tag (gtag.js) - Google Analytics