Head First设计模式之装饰者模式(Decorator Pattern)

前言:

本节将深度讨论继承滥用问题,将会学到使用对象组合的方式,在运行时装饰类,在不修改任何底层代码的情况下,给对象赋予新的职责。

1.   基本需求:咖啡连锁店业务扩张需要重新设计订单系统

背景:由于StarBuzz咖啡连锁店业务扩张,准备更新订单系统,以合乎他们的饮料供应要求。

他们原来的类设计如下:

用户在购买咖啡的时候,可以能会要求在咖啡中加入各种调料,StarBuzz会根据用户加入的不同调收取不同费用,新的订单系统必须考虑到这些调料部分。

1.1 第一次设计

以上的每一个类的Cost()方法将会算出咖啡加上订单的各种调料的价钱。虽然可以满足需求,但是这样会需要很多很多的类,而且也违反了OO的设计原则。

1.2 第二次设计

不需要创建那么多的类,只需要通过利用实例变量和继承,就可以追踪调料。

设计如下:

这样做,确实可以暂时满足需求,但是还会存在一些潜在的隐患,如下:

l  调料价格变动会改变原有代码

l  新增调料,除了加上新增方法外,还需要改变超类中的Cost()方法

l  依赖继承,子类将继承一些对自身并不合适的方法

l  部分需求无法满足:如双倍摩卡咖啡

l  违反了开放—关闭原则

2.   引入装饰者模式

2.1 开发----关闭原则

在上一节的第二次设计中我们可以看出这种设计方法明显的违背了“开发—关闭”原则,那什么是开闭原则呢?定义如下:

开发—关闭原则:类应该是对扩展开放,对修改关闭。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为,这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。遵循开开放—关闭原则,通常会引入新的抽象层次,增加代码的复杂度,需要选择在设计中最可能改变的地方,然后应用开发-关闭原则。

2.2 认识装饰者模式,并以装饰者构造饮料订单

在1中我们已经了解到通过继承无法完全解决问题,这里我们以饮料为主体,然后在运行时以调料来“装饰”饮料。例如:如果客户想要摩卡和奶泡深焙咖啡,如下:

l  拿一个深焙(DarkRoast)对象

l  以摩卡(Mocha)对象装饰它

l  以奶泡(Whip)对象装饰它

l  调用Cost()方法,并依赖委托(delegate)将调料的价钱加上去

说明:以DarkRoast对象开始,顾客想要摩卡(Mocha),所以建立一个Mocha对象,用它将DarkRoast对象包起来,顾客想要奶炮(Whip),所以需要建立一个Whip装饰者,并用它将Mocha对象包起来。最后算钱,通过调用最外层的装饰者Whip的Cost()方法就可以办到。其调用过程如下:

Whip.Cost()àMocha.Cost()àDarkRoast.Cost()(返回DarkRoast的价钱)àMocha.Cost()(Mocha在DarkRoast的结果上加上自己的价钱)àWhip.Cost()(Whip在Mocha的返回结果上加上自己的价钱)

基于以上的这种分析,我们可以得出以下的结论:

装饰者和被装饰对象有相同的超类型。

可以用一个或者多个装饰者包装一个对象。

因为装饰者和被装饰者有相同的超类型,所以在任何需要原始对象的场合,可以用装饰过的对象代替它。

装饰者可以在所委托被装饰者的行为之前或者之后,加上自己的行为,达到特定目的。

对象可以在任何时候被装饰,所以可以在运行时动态的用你喜欢的装饰者来装饰对象。

2.3 定义装饰者模式

装饰者模式:

动态的将责任附加到对象上,若要扩展功能,装饰者提供了更有弹性的替代方案。

设计类图如下:

依据这种装饰者模式的类图,我们设计出StarBuzz的类图,也让它符合这种结构设计,类图如下:

装饰者和被装饰者都继承自Beverage类,也就是拥有共同的超类,这里,我们是利用继承达到“类型匹配”,而不是利用继承获得“行为”。

装饰者和组件组合时,就是在加入新的行为,所得的新行为并不是继承自超类,而是由组合对象得来的。

如果依赖继承,那么类的行为只能在编译时静态决定,行为不是来自超类就是子类覆盖后的版本,每当需要新行为时,还得修改现有代码。如果利用组合,就可以动态的实现新的装饰者增加新的行为。

3 用装饰者模式实现咖啡店需求

根据在2.3中设计的咖啡店的类图,下面就进行具体的编码实现:

3.1 Beverage类(抽象组件)

 /// Description: Beverage抽象类
    /// </summary>
    public abstract class Beverage
    {
        public string description = "UnknownBeverage";
        public abstract stringGetDescription();

        public abstract double Cost();

    }

3.2Condiment(调料)基类(继承自Beverage基类,抽象装饰者)

/// Description:调料基类、派生类
    /// </summary>
    public abstract class CondimentDecorator:Beverage
    {
        //public  abstract string GetDescription();
    }

3.3饮料类(继承Beverage基类,具体组件)

public class Espresso:Beverage
    {
        public Espresso()
        {
            description = "Espresso";//设置饮料的表述,description继承自Beverage类的实例变量
        }

        public override double Cost()
        {
            return 1.99;
        }

        public override string GetDescription()
        {
            return description;
        }
    }

    public class HouseBlend : Beverage
    {
        public HouseBlend()
        {
            description = "HouseBlend";
        }

        public override double Cost()
        {
            return 0.89;
        }

        public override string GetDescription()
        {
            return description;
        }
    }

    public class DarkRoase : Beverage
    {
        public DarkRoase()
        {
            description = "DarkRoase";
        }

        public override double Cost()
        {
            return 1.11;
        }
        public override string GetDescription()
        {
            return description;
        }
    }

    public class Decat : Beverage
    {
        public Decat()
        {
            description = "Decat";
        }

        public override double Cost()
        {
            return 1.22;
        }

        public override string GetDescription()
        {
            return description;
        }
    }

3.4调料类(装饰者)

public class Mocha : CondimentDecorator
    {
        Beverage beverage;
        public Mocha(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string GetDescription()
        {
            return beverage.GetDescription() + ",Mocha";
        }

        public override double Cost()
        {
            return 0.2 + beverage.Cost();
        }
    }

    public class Soy : CondimentDecorator
    {
        Beveragebeverage;
        public Soy(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string GetDescription()
        {
            return beverage.GetDescription() + ",Soy";
        }

        public override double Cost()
        {
            return 0.3 + beverage.Cost();
        }
    }

    public class Whip : CondimentDecorator
    {
        Beverage beverage;
        public Whip(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string GetDescription()
        {
            return beverage.GetDescription() + ",Whip";
        }

        public override double Cost()
        {
            return 0.3 + beverage.Cost();
        }
    }

3.5 测试

Beverage.Beverage beverage = newBeverage.Espresso();
            Console.WriteLine(beverage.GetDescription()+ " $ " + beverage.Cost());

            Beverage.Beverage beverage1 = newBeverage.DarkRoase();
            beverage1 = new Beverage.Mocha(beverage1);
            beverage1 = new Beverage.Mocha(beverage1);
            beverage1 = new Beverage.Whip(beverage1);
            Console.WriteLine(beverage1.GetDescription()+ " $ " + beverage1.Cost());

            Beverage.Beverage beverage2 = newBeverage.HouseBlend();
            beverage2 = new Beverage.Soy(beverage2);
            beverage2 = new Beverage.Mocha(beverage2);
            beverage2 = new Beverage.Whip(beverage2);
            Console.WriteLine(beverage2.GetDescription()+ " $ " + beverage2.Cost());

结果如下图:


4       总结

通过本章的学习,我们可以学到以下知识:

l  OO原则:

封装变化

多用组合,少用继承

针对接口编程,不针对实现编程

为交互对象之间的松耦合设计而努力

对扩展开放,对修改关闭(本章节新学习的OO原则)

l  OO模式

装饰者模式—动态地将责任附加到对象上,想要扩展功能,装饰者提供有别于继承的另一种选择。

l  要点归纳

继承和装饰者都可以让我们扩展行为,但继承不是弹性设计的最佳方案。

装饰者模式意味着一群装饰者类,装饰者类反应了被装饰组件的类型,可以用多个装饰者包装对象。

装饰者可以在被装饰者的行为之前或者之后加上自己的行为,甚至将被装饰者的行为取代,以到达特定目的。

装饰者模式会导致设计中出现许多小对象,过度使用会使程序变得复杂。

时间: 2024-10-10 22:46:42

Head First设计模式之装饰者模式(Decorator Pattern)的相关文章

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

装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能. 通过采用组合.而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能.避免了单独使用继承带来的“灵活性差"和"多子类衍生问题". 优点:装饰类和被装饰类可以独立发

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

装饰者模式(Decorator Pattern) Java的IO类 使用方法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716823 装饰者模式(decorator pattern)参见: http://blog.csdn.net/caroline_wendy/article/details/26707033 Java的IO类使用装饰者模式进行扩展, 其中FilterInputStream类, 就是装饰者(decora

设计模式 - 装饰者模式(Decorator Pattern) 详解

装饰者模式(Decorator Pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者模式(Decorator Pattern):动态地将责任附加到对象上. 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案. 使用方法: 1. 首先创建组件(Component)父类, 所有类,具体组件(Concrete Component)和装饰者(Decorator)都属于这一类型, 可以进行扩展

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

装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者模式(Decorator Pattern):动态地将责任附加到对象上. 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案. 用法: 1. 首先创建组件(Component)父类, 全部类,详细组件(Concrete Component)和装饰者(Decorator)都属于这一类型, 能够进行扩展

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

装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716823 装饰者模式(decorator pattern)參见: http://blog.csdn.net/caroline_wendy/article/details/26707033 Java的IO类使用装饰者模式进行扩展, 当中FilterInputStream类, 就是装饰者(decorato

例说装饰者模式(Decorator Pattern)

前言 装饰者模式在餐饮行业有着比较广泛的应用,网上大部分关于该模式的例子都和饮食相关,以前看译制片电影,每当看到老外们在咖啡店一口流利的点咖啡要加糖要加奶昔要加这加那的时候,感觉好有派~好高大上啊~,为啥我在小卖部都是"来瓶汽水"就没话说了呢~,难道是我不会"装"? 官方定义 动态的给一个对象添加一些职责,就增加功能来说,该模式比生成子类更为灵活--GOF Decorator模式是一种相对简单的对象结构性模式,动态和对象是个对应的关系,正如静态和类这样的对应关系,编

Android设计模式之中的一个个样例让你彻底明确装饰者模式(Decorator Pattern)

导读 这篇文章中我不会使用概念性文字来说明装饰者模式.由于通常概念性的问题都非常抽象.非常难懂.使得读者非常难明确究竟为什么要使用这样的设计模式.我们设计模式的诞生,肯定是前辈们在设计程序的时候遇到了某种困难,为了避免这样的苦难的发生,从而设计出来的这样的设计模式,所以这篇文章中我会带领大家遇见这样的困难,从而使用设计模式解决这样的困难,最后大家就会明确什么是设计者模式,什么时候应该使用设计者模式以及怎样使用设计者模式了 首先我们先来看一下装饰者模式的UML图是什么样子的.图中各个类的含义不懂没

设计模式(结构型)之装饰者模式(Decorator Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之组合模式(Composite Pattern)>http://blog.csdn.net/yanbober/article/details/45392513 概述 装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为.装饰模式是一种用于替代继承

[设计模式] 9 装饰者模式 Decorator

转:http://www.jellythink.com/archives/171#prettyPhoto 什么是装饰模式? 在GOF的<设计模式:可复用面向对象软件的基础>一书中对装饰模式是这样说的:动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能.通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式.显然,直接修改对应的类这

设计模式(三)——装饰器模式(Decorator Pattern)

发现太过于刻意按照计划来写博客,有点不实际,刚好最近在一个网课上复习AOP的知识,讲到了装饰器模式和代理模式,顺便复习总结一下. 首先了解一下装饰器模式,从名字里面可以看出来,装饰器模式就类似于房子装潢吧,比如刚买的毛坯房,只有一个没有门,直接就可以进去. 首先设计一个房子类Room,实现一个进入方法Access /// <summary> /// 抽象接口 用来进行约束 /// </summary> public interface IAccess { void Access()