设计模式 -- 装饰者模式

这个设计模式还是第一次使用,初次使用觉得特别设计特别别扭,还是要好好钻研一番,否则搞不好使用它会给你带来麻烦。

1、什么是装饰者模式?

     装饰者模式能够动态的将功能和职责添加到对象上。听起来觉得很不错呢,可是我们为什么要使用装饰者模式呢?用《Head First》的话说就是解决掉继承的滥用问题,前几个设计模式里我们都有提到“多用组合,少用继承”这个设计原则,这个模式里将继续讨论这个问题。通常我们想到扩展一个类的功能时,总是最先想到“继承”,继承有时候很好用,但是当设计很复杂的类关系时,继承的“黑暗面”会远远大于它的带来的便利。举个例子说奥迪汽车是个基类,这个类有很多它的子类,比如说,A1,A4,A6,A8等等,然后呢,还有带GPS的A1,A4,A6,A8,还有带雷达的A1,A4,A6,A8。有一天啊,我发明了一个先进的防爆系统,专供奥迪车使用,那么A1,A4,A6,A8都要重新加上一个防爆系统;再有一天,奥迪推出了A100,那么接踵而来的就是奥迪A100,带雷达的A100,带GPS的A100,带防爆的A100,带雷达和GPS的A100,带雷达和防爆的A100,带雷达,防爆,和GPS的A100!!!!Oh,No!!简直是太繁琐了,这就是这就是滥用继承引起的“类爆炸”啊!!哈哈,不急不急,想想现实生活中我们是怎么买汽车的吧,我们总是买来汽车,在给汽车安装一些雷达,GPS,防爆系统等设备吧,这就对了,雷达,GPS,防爆系统他们是独立于汽车存在的,是用来装饰汽车的,我们应该把他们抽象出来作为类存在,这样我们想给一个装雷达,或者装GPS不久容易的多了吗?

2、装饰者模式设计原则

     2.1封装变化

          这里面我们可变化的部分是两部分,汽车会变化(总是会有不同型号的汽车被生产);装饰者会变化(总是会有新的装饰者被发明)。那么我们至少已经有了两个被封装的变化了:汽车类 和 装饰者类。那么所谓的A1,A4,A6,A8当然就是汽车的子类,而雷达,GPS,防爆系统就是装饰者的子类喽!

    2.2多用组合,少用继承

        呵呵,结合刚才的叙述,你是不是已经知道这个模式是解决继承带来的“类爆炸”问题的了?那么,这里面的组合又从何而来啊?被装饰了雷达的汽车还是汽车,也就是说汽车有了新的功能之后并没有发生类型的转变,也就是说装饰者也是汽车的子类。

    2.3 对扩展开放,对修改封闭

         我觉得这是装饰者模式最核心的设计思想了,我们在不修改系统底层代码的情况下(一般都是已有的类),来进行功能的扩展。这样的设计弹性很大,系统可以随时接受新功能来应对需求的变化。是不是想到了观察者了?其实这是一个基本的设计原则,很多设计模式都遵循这个原则,如工厂模式,观察者模式等等。

    2.4其他:针对接口编程,而不针对实现编程,对象之间的松耦合设计。

3、如何实现装饰者模式?

    实现装饰者模式需要注意以下问题:

    3.1  装饰者和被装饰者应该具有相同的超类型;

    3.2  可以使用一个或者多个装饰者包装一个对象;

    3.3  装饰者可以在所委托的被装饰者的行为之前与(或)之后,加上自己的行为,已达到特定的目的;

    3.4  既然装饰者和被装饰者有相同的超类型,那么在任何需要原始对象的场合,我们都可以使用被装饰过的对象来替代它。

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

4、装饰者模式类图

我们在具体的看一个例子,背景是一个咖啡店的订单系统,咖啡嘛,我们都知道,分好多种,除此之外,他的配料也分为很多种,具体来看一下使用装饰者模式怎么实现这个系统吧

看到了这个类图是不是觉得比继承好很多呢?

关于刚刚提到的汽车的例子,我们也可以给出类图

 

让我们来看看实现吧

5、代码示例

//饮料类---抽象类
    public abstract class Beverage
    {
        protected string description = "Unknown Beverage";

        public abstract string getDescription();
        public abstract double cost();
    }

//装饰者类 --调料抽象类, 继承自被装饰者(饮料)
    public abstract class ComdimentDecorator:Beverage
    {
        
    }
//低咖啡因咖啡 -- 继承自抽象饮料类
    public class Decaf : Beverage
    {
        public Decaf()
        {
            description = "Decaf coffee";
        }

        public override double cost()
        {
            return 1.05;
        }

        public override string getDescription()
        {
            return this.description;
        }
    }

public class DarkRoast : Beverage
    {
        public DarkRoast()
        {
            description = "Dark Roast Coffee";
        }

        public override double cost()
        {
            return 0.99;
        }

        public override string getDescription()
        {
            return this.description;
        }

    }

//浓缩咖啡类--继承自饮料抽象类
    public class Espresso : Beverage
    {
        public Espresso()
        {
            description = "Espresso";
        }

        public override double cost()
        {
            return 1.99;
        }

        public override string getDescription()
        {
            return this.description;
        }
    }

//黑咖啡 -- 继承自抽象饮料类
    public class Houseblend : Beverage
    {
        public Houseblend()
        {
            description = "House Blend Coffee";
        }

        public override double cost()
        {
            return .89;
        }

        public override string getDescription()
        {
            return this.description;
        }
    }
//奶泡
    public class Whip : ComdimentDecorator
    {
        public Beverage beverage;

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

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

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

//豆浆
    public class Soy : ComdimentDecorator
    {
        public Beverage beverage;

        public Soy(Beverage beverage)
        {
            this.beverage = beverage;
        }
        public override string getDescription()
        {
            return beverage.getDescription() + " , Soy";
        }

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

//牛奶
    public class Milk : ComdimentDecorator
    {
        public Beverage beverage;

        public Milk(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string getDescription()
        {
            return beverage.getDescription() + " , Milk";
        }

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

//摩卡
    public class Mocha : ComdimentDecorator
    {
        public Beverage beverage;//记录被装饰者

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

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

        public override double cost()
        {
            return 0.20 + beverage.cost();
        }
    }
//测试代码
        static void Main(string[] args)
        {
            //实例化一杯不需要调料的浓缩咖啡
            Beverage be1 = new Espresso();
            Console.WriteLine(be1.getDescription() + ",  $" + be1.cost());
            Console.WriteLine();

            //双倍摩卡加奶泡的烧烤咖啡
            Beverage be2 = new DarkRoast();
            be2 = new Mocha(be2);
            be2 = new Mocha(be2);
            be2 = new Whip(be2);
            Console.WriteLine(be2.getDescription() + ",  $" + be2.cost());
            Console.WriteLine();

            //加豆浆的综合咖啡
            Beverage be3 = new Houseblend();
            be3 = new Soy(be3);
            Console.WriteLine(be3.getDescription() + ",  $"+be3.cost());

            Console.ReadKey();
        }
    }

关于装饰者这个类我们到底采用抽象类还是接口是无所谓的,我个人认为在这里没什么区别,当然也有的人直接采用类来实现,可以达到目的,但是设计不好,毕竟我们应该遵循针对接口编程,不针对实现编程这个设计的原则。所有的原始设计都来源于生活,但最终成熟的设计应该是高于生活的。

 

     

posted on 2013-03-30 10:58  雨过晴空  阅读(353)  评论(0编辑  收藏  举报

导航