装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。java
从定义来看,除了提到比继承更有弹性,其余的仍是很是模糊,下面就先给出其UML类图。程序员
从UML类图能够看到装饰者基类(Decorator)主要使用了一个其基类的组合,另外装饰者分别含有本身的新特性,如ConcreteDecoratorA,有本身特有的字段,ConcreteDecoratorB有本身特有的方法。该模式设计的巧妙的地方就在于以上的特色。算法
为何说巧妙呢?由于装饰者都含有一个Component的引用,就比如你是老板要找c#开发的,遇到了一个不只会c#的,还会java的你同样,这样的好处是无可置疑的,不只可让你作asp.net,若是哪一天公司要开发andriord了,你照样能够胜任,而不须要再去找andriord的童鞋了,固然也多是不只具备C#技能,还多是能说会道的你。若是哪一天本身想跑业务,本身随时均可以去。跑累了,想体验一下代码生活,照样继续干代码工做……在这个过程当中,就像本身被知识武装了起来,因此叫作装饰者模式。c#
固然有老板要找个什么都会的,所有集中在了一我的身上,也有必定的缺陷,或许一会让你搞andriord,一会让你去搞c#,一会让你跑业务,这样可能会有影响的。敲代码刚刚有思路,就让你去跑业务,显然是有些不科学。设计模式
既要武装本身,又要不能老板不时地打扰你的思路,这就须要一个原则:asp.net
类应该对扩展开放,对修改关闭。ide
就是要鼓励学习新东西,可是要禁止打断写代码的思路。这样才能提升效率,那人手不够怎么办,找个新人。学习
说了这么多,仍是以为经过一个例子才能更好的说明。测试
下面就根据head first设计模式书中的例子,来看看冷饮店是如何卖饮料的。this
刚刚开店,只卖矿泉水(MineralWater)和咖啡(Coffee),价钱直接就调用一下其cost()方法就能够的出来。但是聪明的老板必定会想到若是加一下白糖(Sugar)或者牛奶(Milk)是否是能够多赚些呢?可是算帐很差算。不可能让矿泉水和咖啡中添加两个方法,AddSugar和AddMilk,这样会产生的问题,若是再加调料,会继续修改饮料的类,这样好不容易写好的类,可能就要继续就改,总共的价格可能也要修改。实在不是好办法。
下面就来看看装饰者模式怎么处理这样的问题。
若是你既是c#程序员,也是andriord程序员,老板说了,我不打断你的思路,白天你能够作C#项目,晚上作andriord的项目,工资的算法你说了算。
你说晚上是白天的2倍,老板说好,财务说,我只看你晚上有没有干,晚上含有白天的引用,计算方法:WageofNight()+Day.wageOfDay()。
一样的道理咖啡店也会经过这样的方式,调料引用一个饮料,总价格经过饮料的价钱加上引用调料的价格。
好了,上图了。咖啡店中饮料类图以下:
感受不上代码,仍是会以为少些什么,下面就给出对应的代码:
public abstract class Beverage { public string description = "全部饮料的基类"; public abstract double Cost(); public virtual string GetDesciption() { return description; } }
public class Coffee : Beverage { public Coffee() { description = "我是热咖啡"; } public override double Cost() { return 1.50; } }
public class MineralWater : Beverage { public MineralWater() { description = "我是矿泉水"; } public override double Cost() { return 1.0; } }
public abstract class BeverageDecrator : Beverage { public Beverage beverage; public abstract string GetDesciption(); }
public class Milk : BeverageDecrator { public Milk(Beverage beverage) { this.beverage = beverage; } public override string GetDesciption() { return beverage.description + "-------加牛奶"; } public override double Cost() { return beverage.Cost() + 0.5; } }
public class Sugar : BeverageDecrator { public Sugar(Beverage beverage) { this.beverage = beverage; } public override string GetDesciption() { return beverage.description + "--------加糖"; } public override double Cost() { return 0.5 + beverage.Cost(); } }
测试代码:
class Program { static void Main(string[] args) { Console.WriteLine("小二哥,来一杯矿泉水加糖"); Beverage mineralWater = new MineralWater(); Sugar sugar = new Sugar(mineralWater); Console.WriteLine("客官,来了,请慢用"); Console.WriteLine(sugar.GetDesciption()); Console.WriteLine("小二哥,结帐"); Console.WriteLine("客官,共"+sugar.Cost()+"¥"); Console.WriteLine("客官慢走"); Console.WriteLine("----------------------------"); Console.WriteLine("小二哥,来一杯咖啡加牛奶"); Beverage coffee = new Coffee(); Milk milk = new Milk(coffee); Console.WriteLine("客官,来了,请慢用"); Console.WriteLine(milk.GetDesciption()); Console.WriteLine("小二哥,结帐"); Console.WriteLine("客官,共" + milk.Cost() + "¥"); Console.WriteLine("客官慢走"); Console.ReadKey(); } }
请注意上述代码是如何使用组合从而达到能够自由的扩展以及修改的,最终的能够互不影响的扩展和修改。为了进一步演示自由的扩展和修改的,进一步提需求:如今我以为加牛奶要贵些,而后送一张优惠券。
其余的不改变,只改变Milk类,便可。
public class Milk : BeverageDecrator { public Milk(Beverage beverage) { this.beverage = beverage; } public override string GetDesciption() { return beverage.description + "-------加牛奶"; } public override double Cost() { SendCoupon(); return beverage.Cost() + 2; } public void SendCoupon() { Console.WriteLine("送优惠券一张"); } }
输出结果:
看看如今修改了牛奶类,没有改变其余地方,就能够获得咱们想要的结果了。从而实现了前面所说的原则,开闭原则。
上面的设计好像是遵循了开闭原则,其实则没有,由于类要对扩展开放,对修改关闭,咱们很明显,对修改没有关闭,只是对扩展开放而已,如今就来解决修改关闭的问题,要到达这样的效果,就肯定了不能改变类的任何地方,只能对类进行扩展,是经过另一个类,来进行对改类的扩展,这样作的目的是不只扩展了想要扩展的类,还能够灵活的扩展本身想扩展的类。已达到复用的效果。接下来,看看如何在不改变原有的基础上进行扩展,从而遵循开闭原则。
注意上面的BeverageExtend类,其类多了个SendCoupon方法,(这个方法也是为了显眼而已),用来送优惠券,咱们如今修改的是sugar类或Milk类,可是经过的是BeverageExtend类来扩展的,这样才是真正到达了开闭原则。
下面给出扩展类和测试代码
public class BeverageExtend : BeverageDecrator { public BeverageExtend(Beverage beverage) { this.beverage = beverage; } public virtual void SendCoupon() { Console.WriteLine("总价为" + this.Cost()); Console.WriteLine("送您一张优惠券"); } public override string GetDesciption() { return beverage.GetDesciption() + "——————加优惠券"; } public override double Cost() { return beverage.Cost() + 1.5; } }
测试代码:
static void Main(string[] args) { Console.WriteLine("小二哥,来一杯矿泉水加糖"); MineralWater mineralWater = new MineralWater(); Sugar sugar = new Sugar(mineralWater); Console.WriteLine("客官,来了,请慢用"); Console.WriteLine(sugar.GetDesciption()); Console.WriteLine("小二哥,结帐"); Console.WriteLine("客官,共"+sugar.Cost()+"¥"); Console.WriteLine("客官慢走"); Console.WriteLine("----------------------------"); Console.WriteLine("小二哥,来一杯矿泉水加糖"); MineralWater mineralWater1 = new MineralWater(); Sugar sugar1 = new Sugar(mineralWater1); BeverageExtend beverage = new BeverageExtend(sugar1); Console.WriteLine("客官,来了,请慢用"); Console.WriteLine(beverage.GetDesciption()); Console.WriteLine("小二哥,结帐"); Console.WriteLine("客官,共" + beverage.Cost() + "¥"); Console.WriteLine("客官慢走"); Console.WriteLine("----------------------------"); Console.WriteLine("小二哥,来一杯咖啡加牛奶"); Coffee coffee = new Coffee(); Milk milk = new Milk(coffee); BeverageExtend beverage2 = new BeverageExtend(milk); Console.WriteLine("客官,来了,请慢用"); Console.WriteLine(beverage2.GetDesciption()); Console.WriteLine("小二哥,结帐"); Console.WriteLine("客官,共" +beverage2.Cost() + "¥"); Console.WriteLine("客官慢走"); Console.ReadKey(); }
下面给出测试结果图:
本文主要经过装饰者模式,引出了设计模式中的开闭原则,经过冷饮店的售价,来体现装饰者如何实现开闭原则的,可是须要注意要对类进行扩展是经过另一个装饰类来完成的,而不是经过在自己类中实现的。