考虑这样一个场景:咖啡厅需要做一个订单系统,其中必不可少的功能是:获取每种咖啡的价格以及描述。最初设计如下:
但是,每种咖啡可以根据顾客的选择,添加不同的调料,甚至可能是两份同种调料,例如:牛奶,摩卡,豆浆等。这样,如果采用上面的设计,类图会是这样的:
这样设计的缺陷非常明显:类的数量过于庞大,但是问题还不仅仅如此,当某一个原料的价格上扬了,比如牛奶,那么所有包含牛奶的咖啡的cost方法都需要重新修改,这无疑是一场灾难。
一种改进的设计如下:
这样设计类少多了,但是记住,在软件开发中,永远不变的是“改变”,当下面的情况发生改变时,我们必须修改较多代码:1.调料价格改变,2.增加一种调料,3.可能开发出一种新的饮料,不能加入四种调料中的任何一种,4.双倍摩卡咖啡不能实现。
装饰者模式动态的将责任附加到对象上。若要扩展功能,装饰者模式提供了比继承更有弹性的替代方案。
这样,如果需要一个双倍摩卡加牛奶的DarkRoast,只需要 new Milk(new Mocha(new Mocha(new DarkRoast())));咖啡的配料完全在运行时决定。计算价格的方法就是外层调用里层,一层层求和即可。可以任意选择配料的种类,数量,并且在运行时决定,类的数量也不多。
这种设计模式被用在Java I/O库中,比如:InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("in.txt"))); IO库这样设计,可以使用户任意组合他所需要的IO功能,只需要对Stream进行包装即可。
要点:
继承是扩展形式之一,但不见得是达到弹性设计的最佳方式。 在设计中,应该允许行为被扩展,而无需修改现有代码。 组合和委托可以用于在运行时动态地加上新的行为。 可以在程序中使用任意个装饰器包装一个对象。 装饰者模式会导致设计中出现许多小类,如果过度使用,会使程序变得复杂。
设计原则:
对扩展开放,对修改关闭。