设计模式学习之三装饰者模式(Decorator)——解决星巴兹扩张快的问题

来源:互联网 发布:国际漫游关闭移动数据 编辑:程序博客网 时间:2024/06/05 01:53

 

      星巴兹是以扩张速度最快而闻名的咖啡连锁店。如果你在街角看到它的店,在对面街上肯定还会看到另一家。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的:

 

购买咖啡时,也可以要求在其中加入各种调料,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。星巴兹会根据所加入的调料收取不同的费用。所以订单系统必须考虑到这些调料部分。这是他们的第一个尝试……

好吧,这简直是“类爆炸”。星巴兹为自己制造了一个维护噩梦,如果牛奶的价钱上扬怎么办?新增一种调料风味时,怎么办?

好吧,有人认为上面的设计“笨透了”(这是事实),利用实例变量和继承,就可以追踪这些调料。那我们试试吧,设计类图如下:

但是如果用上面的设计,如果调料价钱改变会使我们更改现有的代码,一旦出现新的调料,我们需要加上新的方法,并且改变超类中的cost()方法。。。。。。上面违背了一个非常重要的原则之一:类应该对扩展开放,对修改关闭。

       到了解决真正问题的时候了,在解决问题之前先让我们去认识一下装饰者模式,也就是解决这个问题需要用到的模式。解决问题的办法就是以饮料为主题,然后在运行时以调料来“装饰”饮料。装饰者模式动态地将责任附加到对象上。想要扩展功能,装饰者提供了有别于继承的另一种选择。类图如下:

好吧,现在要开始装饰我们的饮料了,让星巴兹饮料也能符合此框架:

从上面我们能看出:

1. 装饰者和被装饰对象有相同的超类型。

 

2. 可以用一个或多个装饰者包装一个对象。

3. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。

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

5. 装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。

       现在该是把设计变成真正代码的时候了!先从Beverage类下手,这不需要改变星巴兹原始的设计:

 

Beverage类很简单,让我们也来实现Condiment(调料)抽象类,也就是装饰者类:

 

现在已经有了基类,让我们开始实现一些饮料吧。别忘了我们需要为具体的饮料设置描述,而且还必须实现cost()方法:

 

 

 

 

如果你回头看类图,现在我们已经实现了抽象组件(Beverage),有了具体组件(HouseBlend。。。),也有了抽象装饰者(CondimentDecorator),现在让我们来实现具体装饰者:

 

 

 

OK,现在我们可以坐下来来享受劳动成果了,点几种咖啡尝尝,下订单吧:

 

      真实世界也存在诸多的装饰者,学过Java IO的就知道IO就采用了装饰者模式。下面是装饰的IO类图:

既然你已经知道装饰者模式,也看了Java IO的类图了,那么我们是否可以试着编写自己的输入装饰者了,那么试着编写一个装饰者吧,把输入流的所有大写字符转换成小写:

 

测试代码:

 

 

OK,装饰者模式到此算告一段落了。。。。

 

 

原创粉丝点击