装饰者模式(Decorator)

首先来看一个例子:

比如,饮料可以分为很多种类,而这里我取一个咖啡,那么这个咖啡呢,有多种形式的,

比如有加糖了的咖啡,有加奶的咖啡,也有加热了的咖啡,也有加了冰块的咖啡。

而各个顾客的选择却是不同的,比如,有的客户是要加糖的咖啡,而有的客户却是要加冰的咖啡,

也就是需求是各种各样的,那么如何实现这种情况呢?

先来看一种愚笨的做法,那就是你可以通过继承来实现,

image

虽然上面的做法确实可以提供加糖加冰的咖啡,但是,这种方法也太拙劣了吧,

如果我要加糖和加热的咖啡呢?如果我还有更多的需求呢?

如果我新增饮料巧克力奶茶,然后其中也可以加糖,加奶,加热,加冰呢?

如果使用上面的方式来完成上面提到的种种需求的话?

My God !!!

其实从上面的例子中反映出来的问题是,

确实可以通过继承来实现扩展(比如,由咖啡扩展成了加了糖的咖啡),

但是并不一定说使用继承就可以达到弹性设计。

为了解决上面出现的问题,一种新的设计模式应运而生,那就是装饰者模式。

装饰者模式动态的给一个对象添加一些责任(也就是功能),若要扩展功能的话,

装饰者提供了比继承更有弹性的替代方案,因为装饰者模式比生成子类更加灵活。

首先来看一下装饰者模式的结构图

image

下面就来解释装饰者模式了

装饰者模式呢,其实可以看做是一种在已有功能上动态添加新的功能的一种方式,

在不用装饰者模式的前提下,如果要在已有的功能上添加新功能,一般都是可以使用继承的,

但是,继承的缺点呢,在上面的咖啡的例子中也暴露的很明显,同时,使用继承的话,

添加功能不是动态的,因为子类完全继承了父类,

而使用装饰者模式的话,您可以在客户端按照需求一个一个的包装对象,

通过包装对象来添加新功能,

这样便实现了动态添加新功能,

比如,我可以对 Component 通过 ConcreteDecoratorA 来包装一个 State 状态,

或者是通过 ConcreteDecoratorB 来包装一个新的行为(功能)Behavior ,

这样便实现了功能的动态添加。

又比如上面的咖啡的例子,就可以使用装饰者模式来实现,我先是对咖啡使用加糖来包装,

这样就可以得到加糖的咖啡,而后再使用冰来包装加了糖的咖啡,这样就可以得到加了糖,

并且加了冰的咖啡。

其实光这样说是感受不到多少装饰者模式带来的优点的

下面就将上面提到的咖啡这个例子写出来看看吧,这样理解起来便清晰多了

image

先来看 Drink 类

using System;

namespace Decorator
{
    public abstract class Drink
    {
        /// <summary>
        /// 在抽象类中只定义了一个抽象接口
        /// 然后可以通过这个抽象接口来给对象动态的添加功能
        /// </summary>

        public abstract void ShowDrink();
    }
}

然后就是一个 Coffee 类

using System;

namespace Decorator
{
    public class Coffee : Drink
    {
        private string name;

        public Coffee(string name)
        {
            this.name = name;
        }

        /// <summary>
        /// 这里呢,也可以给当前的对象添加一些功能
        /// 比如这里就是指定了咖啡的名称
        /// 不过在这里添加的功能是静态添加的
        /// </summary>

        public override void ShowDrink()
        {
            Console.WriteLine("咖啡名称为:{0}    ", this.name);
        }
    }
}

下面再来看 DecoratorDrink 类

namespace Decorator
{
    public class DecoratorDrink : Drink
    {
        /// <summary>
        /// 在装饰类中必须要保存一个对于对象的引用
        /// 其是继承自 Drink 这个抽象类
        /// 它是从外类来扩展 Drink 类
        /// 所以 Drink 类并不知道 DecoratorDrink 类的存在
        /// </summary>

        protected Drink drink;

        public DecoratorDrink(Drink drink)
        {
            this.drink = drink;
        }

        public override void ShowDrink()
        {
            if (drink != null)
            {
             
  //必须执行父类的 ShowDrink
                //其主要是通过多态来实现的,因为 Coffee 也是 Drink 类型

                drink.ShowDrink();
            }
        }
    }
}

还有就是装饰类 Sugar

using System;

namespace Decorator
{
    public class Sugar : DecoratorDrink
    {
        /// <summary>
        /// 必须要有所要扩展的对象
        /// 比如扩展咖啡的话,必须要存在咖啡这个对象
        /// 同时,这里直接调用了父类的构造函数
        /// </summary>
        /// <param name="drink"></param>

       
public Sugar(Drink drink)
            : base(drink)

        {

        }

        public override void ShowDrink()
        {
           //首先必须要调用父类的 ShowDrink
            base.ShowDrink();
            //然后下面就可以添加新功能了
            Console.WriteLine("加糖   ");
        }
    }
}

装饰类 Milk

using System;

namespace Decorator
{
    public class Milk : DecoratorDrink
    {
        public Milk(Drink drink)
            : base(drink)
        {

        }

        public override void ShowDrink()
        {
            base.ShowDrink();
           //添加新功能
            Console.WriteLine("加奶   ");
        }
    }
}

装饰类 Ice

using System;

namespace Decorator
{
    public class Ice : DecoratorDrink
    {
        public Ice(Drink drink)
            : base(drink)
        {

        }

        public override void ShowDrink()
        {
            base.ShowDrink();
            //添加新功能
            Console.WriteLine("加冰   ");
        }
    }
}

装饰类 Hot

using System;

namespace Decorator
{
    public class Hot : DecoratorDrink
    {
        public Hot(Drink drink)
            : base(drink)
        {

        }

        public override void ShowDrink()
        {
           base.ShowDrink();
            //添加新功能
            Console.WriteLine("加热   ");
        }
    }
}

最后就是 Main 函数了

using System;
using Decorator;

namespace DecoratorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Coffee coffee = new Coffee("咖啡一号");
            //给咖啡加糖,也就是使用糖来装饰咖啡
            Sugar sugar =
new Sugar(coffee);
            //给加了糖的咖啡加热,也就是使用加热来装饰咖啡
            Hot hot = new Hot(sugar);
            //显示出当前咖啡的状态
            hot.ShowDrink();

            Console.WriteLine();

            coffee = new Coffee("咖啡二号");
            sugar = new Sugar(coffee);
            //给加了糖的咖啡加奶
            Milk milk = new Milk(sugar);
            //给加了糖和奶的咖啡加冰块
            Ice ice = new Ice(milk);
            ice.ShowDrink();

            Console.ReadLine();
        }
    }
}

最后当然是要看一下效果咯

image

从 Main 函数中可以看出,我可以方便的通过将各个装饰类组合起来(一个个的新功能添加起来)而成为新产品,

上面呢,就是装饰者模式的一个最基本的应用和介绍了,

其实,装饰者模式呢,它把类中的一些装饰功能移除,再将这些装饰功能写到另外的类中

这样可以简化核心类的复杂度,同时,面对不同的需求时,可以提供代码得复用,

否则,你要完成需求“咖啡加糖”的话,就得写一个咖啡加糖类,再变点需求又得变一大堆,

通过装饰模式,可以有效的将类的核心功能和装饰功能分离开来,比如上面的 Demo 的核心功能是咖啡,

而装饰功能就是加糖,加奶,加热,加冰这些了,

同时,通过装饰者模式,你可以很好的扩展自己的设计

比如,上面的 Demo ,如果在后面再来需求,说可以加盐(应该不会有这种需求吧?哈哈哈),

那么你只需再完成一个加盐的装饰类,然后再在客户端动态添加加盐这项新功能就可以了,

而上面提到的这些,用继承几乎上无法避免违背开闭原则。

同时,在这里还必须提一下的就是,装饰模式的装饰顺序很重要

在上面的 Demo 中无法体现出装饰模式装饰顺序的重要性,

再来看一个装饰的例子,以用来体现出装饰模式装饰顺序的重要,

那就是一个人,它穿衣服,其实是可以用装饰模式来解决的,

因为你可以把内衣,外衣,内裤,外裤,鞋子等等内容都看做一个个的装饰类,

那么这里,装饰顺序就显得比较重要了,

比如,如果你先装饰了外裤,然后再装饰内裤,那就麻烦大了,

因为你变成了超人---典型的内裤外穿~~~~~

从这里可以看出装饰顺序还是很重要的!!!

上面提到的都是装饰模式的优点,而事实上,任何事物都是两面的,

有利必有弊,装饰模式也一样的,

那就是使用装饰模式,会使整个的设计当中出现很多的小类,也就是许许多多的装饰类

比如,上面的关于咖啡的 Demo 中就出现了 4 个小的装饰类,

如果,一个设计中,装饰模式过度使用的话,会平添很多的小类,

那样的话,会让程序变得更加复杂

关于装饰模式呢,就谈到这里了~~~

 

posted @ 2010-05-04 17:34  小宝马的爸爸  阅读(1487)  评论(3编辑  收藏  举报