1 深入浅出装饰模式 一、引子 装饰模式?肯定让你想起又黑又火的家庭装修来。其实两者在道理上还是有很多相像的地方。家庭装修无非就是要掩盖住原来实而不华的墙面,抹上一层华而不实的涂料,让生活多一点色彩。而墙还是那堵墙,他的本质一点都没有变,只是多了一层外衣而已。 那设计模式中的装饰模式,是什么样子呢? 通常 OO 程序员会用继承的方法来扩展类的功能,并进而解决所面临的问题。但继承的威力最多也只能到编译时刻为止,运行时它也无能为力了(这里还没有说继承可能带来的麻烦,我们将在后面见到)。但是能够在运行时对类功能进行扩展那才叫“牛”。且看装饰模式是如何做到这点的。 二、定义和结构 装饰模式(Decorator Pattern )也叫包装器模式(Wrapper Pattern)。GoF 在《设计模式》一书中给出的定义为:动态地给一个对象添加一些额外的职责。换而言之,装饰类为子类扩展新功能提供了一种灵活的替换机制,Decorator 模式比生成子类更为灵活。 这里有一家著名的咖啡店 Starbuzz,正在抓狂地更新其订货系统,以适应不断增长的供货要求。 2 为了是咖啡更好喝,或更符合你的口味,你可以向咖啡店要多种调味品,如steamed milk、soy、mocha,或巧克力等等(当然这都是要付钱的,否则就不会有我们这里的问题的 )。Starbuzz 咖啡店为了调味品计费的问题头都大了,因为它们都要单独处理才行,请看他们的第一个设计: 怎么样?类爆炸了吧!这是因为每种咖啡中的cost( ) 方法都要考虑其订单中的不同的调味料计费问题。 3 上面的设计显然太蠢了,看看一个更聪明的OO 程序员是如何设计的。他将设计一些实例变量,并用继承超类的方法来追踪调味料的情况。 然后再添加子类,它们各自对应于菜单上的一种咖啡(或其它饮料): 相关代码如下,其中最关键的是cost( )方法的代码: 4 看起来还不错,一共也只有5 个类,cost( )方法也不算太复杂。那么这样的设计行吗?请想想下列问题: 当每种调料的价格发生变化怎么办?——好象问题不大,我们还不必改写代码! 如果要加入新的调味料该怎么办?——我们不得不增加新方法并修改超类中的cost( )方法! 咖啡店还可以卖别的饮料。如 Ice Tea,调味料是不适用于它们的,但子类IceTea继承 hasWhip( )这样的方法仍然是非常合理的。千万不要忘记分类学的原则,想想上述设计违反了分类学的哪些原则? 如果客人想要两份 mocha 调料,该怎么办...