zoukankan      html  css  js  c++  java
  • 装饰者模式(Decorator)

    首先来看一个例子:
    比如,饮料可以分为很多种类,而这里我取一个咖啡,那么这个咖啡呢,有多种形式的,
    比如有加糖了的咖啡,有加奶的咖啡,也有加热了的咖啡,也有加了冰块的咖啡。
    而各个顾客的选择却是不同的,比如,有的客户是要加糖的咖啡,而有的客户却是要加冰的咖啡,
    也就是需求是各种各样的,那么如何实现这种情况呢?
    先来看一种愚笨的做法,那就是你可以通过继承来实现,

     

    虽然上面的做法确实可以提供加糖加冰的咖啡,但是,这种方法也太拙劣了吧,
    如果我要加糖和加热的咖啡呢?如果我还有更多的需求呢?
    如果我新增饮料巧克力奶茶,然后其中也可以加糖,加奶,加热,加冰呢?
    如果使用上面的方式来完成上面提到的种种需求的话?
    My God !!!
    其实从上面的例子中反映出来的问题是,
    确实可以通过继承来实现扩展(比如,由咖啡扩展成了加了糖的咖啡),
    但是并不一定说使用继承就可以达到弹性设计。
    为了解决上面出现的问题,一种新的设计模式应运而生,那就是装饰者模式。
    装饰者模式动态的给一个对象添加一些责任(也就是功能),若要扩展功能的话,
    装饰者提供了比继承更有弹性的替代方案,因为装饰者模式比生成子类更加灵活。

     

     

    下面就来解释装饰者模式了
    装饰者模式呢,其实可以看做是一种在已有功能上动态添加新的功能的一种方式,
    在不用装饰者模式的前提下,如果要在已有的功能上添加新功能,一般都是可以使用继承的,
    但是,继承的缺点呢,在上面的咖啡的例子中也暴露的很明显,同时,使用继承的话,
    添加功能不是动态的,因为子类完全继承了父类,
    而使用装饰者模式的话,您可以在客户端按照需求一个一个的包装对象,
    通过包装对象来添加新功能,
    这样便实现了动态添加新功能,
    比如,我可以对 Component 通过 ConcreteDecoratorA 来包装一个 State 状态,
    或者是通过 ConcreteDecoratorB 来包装一个新的行为(功能)Behavior ,
    这样便实现了功能的动态添加。
    又比如上面的咖啡的例子,就可以使用装饰者模式来实现,我先是对咖啡使用加糖来包装,
    这样就可以得到加糖的咖啡,而后再使用冰来包装加了糖的咖啡,这样就可以得到加了糖,
    并且加了冰的咖啡。
    其实光这样说是感受不到多少装饰者模式带来的优点的
    下面就将上面提到的咖啡这个例子写出来看看吧,这样理解起来便清晰多了

     

    先来看 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(); 
            } 
        } 
    }
    最后当然是要看一下效果咯
     
    从 Main 函数中可以看出,我可以方便的通过将各个装饰类组合起来(一个个的新功能添加起来)而成为新产品,
    上面呢,就是装饰者模式的一个最基本的应用和介绍了,
    其实,装饰者模式呢,它把类中的一些装饰功能移除,再将这些装饰功能写到另外的类中
    这样可以简化核心类的复杂度,同时,面对不同的需求时,可以提供代码得复用,
    否则,你要完成需求“咖啡加糖”的话,就得写一个咖啡加糖类,再变点需求又得变一大堆,
    通过装饰模式,可以有效的将类的核心功能和装饰功能分离开来,比如上面的 Demo 的核心功能是咖啡,
    而装饰功能就是加糖,加奶,加热,加冰这些了,
    同时,通过装饰者模式,你可以很好的扩展自己的设计
    比如,上面的 Demo ,如果在后面再来需求,说可以加盐(应该不会有这种需求吧?哈哈哈),
    那么你只需再完成一个加盐的装饰类,然后再在客户端动态添加加盐这项新功能就可以了,
    而上面提到的这些,用继承几乎上无法避免违背开闭原则。
    同时,在这里还必须提一下的就是,装饰模式的装饰顺序很重要
    在上面的 Demo 中无法体现出装饰模式装饰顺序的重要性,
    再来看一个装饰的例子,以用来体现出装饰模式装饰顺序的重要,
    那就是一个人,它穿衣服,其实是可以用装饰模式来解决的,
    因为你可以把内衣,外衣,内裤,外裤,鞋子等等内容都看做一个个的装饰类,
    那么这里,装饰顺序就显得比较重要了,
    比如,如果你先装饰了外裤,然后再装饰内裤,那就麻烦大了,
    因为你变成了超人---典型的内裤外穿~~~~~
    从这里可以看出装饰顺序还是很重要的!!!
    上面提到的都是装饰模式的优点,而事实上,任何事物都是两面的,
    有利必有弊,装饰模式也一样的,
    那就是使用装饰模式,会使整个的设计当中出现很多的小类,也就是许许多多的装饰类
    比如,上面的关于咖啡的 Demo 中就出现了 4 个小的装饰类,
    如果,一个设计中,装饰模式过度使用的话,会平添很多的小类,
    那样的话,会让程序变得更加复杂
    关于装饰模式呢,就谈到这里了~~~

  • 相关阅读:
    线程池七大参数介绍
    线程池的三个使用方式
    线程池使用及优势
    css selector 定位
    xpath 定位小技巧
    centos7部署web测试环境 jdk,tomcat,mysql
    Java 访问修饰符
    webdriver的handle 切换窗口
    P1392 取数
    P3414 SAC#1
  • 原文地址:https://www.cnblogs.com/woshilulin/p/3720187.html
Copyright © 2011-2022 走看看