星巴克:世界闻名的咖啡连锁店。他的订单系统,如果你有兴趣了解下的话,跟我来吧。
一、他们原先的设计
//Beverage(饮料)是一个抽象类,店里所提供的饮料都必须继承此类。 public abstract class Beverage { public string description = "Unknown Beverage"; //description(描述),店里面所有饮料的描述。 public string getDescription() //获取饮料描述 { return description; } public abstract double cost();//价格,由子类自己实现。 }
HouseBlend(家常咖啡):星巴克首选咖啡。
public class HouseBlend:Beverage { public HouseBlend() { description = "House Blend Coffee"; } public override double cost() { return 0.89; } }
Espresso(特浓咖啡)素有“咖啡之魂”的美称。对于咖啡馆来说,“没卖 ‘Espresso’ 的咖啡馆就不是咖啡馆”,可见 “Espresso“ 重要性。
public class Espresso:Beverage { public Espresso() { description = "Espresso"; } public override double cost() { return 1.99; } }
现在市场需求出现新的变化:我要红豆特浓咖啡(特浓咖啡+红豆)
加个新类HongDouEspresso 如下?那么如果我换个口味(即新的配料+特浓咖啡)要豆浆特浓咖啡(豆浆+特浓咖啡),那不是又要新建一个类?如果我一天换个口味,那么这个系统存在的每一天不是都要升级一次?这是多么恐怖且二逼的系统啊!!!!
那么,有没有办法把这些配料“动态地”加到咖啡里面来,而不是像下面的代码那样写死在代码中。
public class HongDouEspresso:Beverage { public Espresso() { description = "HongDouEspresso"; } public override double cost() { return 2.29; } }
再想想我们OO设计原则
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。2.针对接口编程,而不是针对实现编程。3.多用组合,少用继承。4.为了交互对象之间的松耦合设计而努力。5.类应该对扩展开发,对修改关闭。二、星巴克订单系统二代出来了:装饰者模式
1 public abstract class Beverage//饮料的抽象类,即组件的抽象类2 {3 public string description = "Unknown Beverage";4 public virtual string getDescription() //用虚方法,因为我想要把后面加进来的配料名称也显示出来。5 {6 return description;7 }8 public abstract double cost();9 }
5.类应该对扩展开发,对修改关闭。所以特浓咖啡,家常咖啡的类代码不变。如下
//特浓咖啡 public class Espresso:Beverage { public Espresso() { description = "Espresso"; } public override double cost() { return 1.99; } }
//家常咖啡public class HouseBlend:Beverage { public HouseBlend() { description = "House Blend Coffee"; } public override double cost() { return 0.89; } }
神奇的配料代码来了
//配料的抽象类 public abstract class CondimentDecorator:Beverage { //每个配料都要实现的抽象类,继承Beverage 是为了保存对某个Beverage 对象的引用,实现多态的“类型匹配”。 }
//红豆(HongDou配料的实现代码) public class HongDou:CondimentDecorator { Beverage beverage;//用实例变量来记录饮料(“这些配料“动态地”加到咖啡里面来,而不是像下面的代码那样写死在代码中。”关键实现) //把饮料当做构造函数参数,是为了把饮料记录到实例变量中。(“这些配料“动态地”加到咖啡里面来,而不是像下面的代码那样写死在代码中。”关键实现) public HongDou(Beverage beverage) { this.beverage = beverage; } public override string getDescription() { return beverage.getDescription() + ",HongDou";//我们希望不仅记录饮料,还记录配料。 } public override double cost() { //要计算带HongDou配料的饮料价格,首先把调用委托给被装饰对象(组件),以计算价格,如何加上HongDou价格,得到最终价格。 return 0.35 + beverage.cost(); } }
//豆浆(DouJiang)的代码跟红豆(HongDou) 代码类似,就不多做阐述了。public class DouJiang:CondimentDecorator { Beverage beverage; public DouJiang(Beverage beverage) { this.beverage = beverage; } public override string getDescription() { return beverage.getDescription() + ",DouJiang"; } public override double cost() { return 0.20 + beverage.cost(); } }
重要时刻:调试下--小二,给爷上一大杯 红豆双豆浆家常咖啡!
static void Main(string[] args) { Beverage beverage2 = new HouseBlend();//创建一个组件对象--家常咖啡 beverage2 = new DouJiang(beverage2);//豆浆装饰 beverage2 = new DouJiang(beverage2); beverage2 = new HongDou(beverage2);//红豆装饰 Console.WriteLine(beverage2.getDescription() + " " + "$" + beverage2.cost()); Console.ReadKey(); }
小二:来了,大爷1.64美刀。
到这来星巴克的订单系统之装饰者模式基本是说完了。
突然,大爷冒出来说:小二,我买这么多,你也不给我打个折什么的啊?至少豆浆给我打个七五折吧?
我们装饰者模式不是万能的,遇到这种情况就不好解决。那怎么办?咱们先回去想想!!!