超越设计模式
深入探讨超越设计模式之外的设计原则
相信大部分人都会这么设计,但是这个设计有什么问题呢?我们看待问题需要以发展的眼光,假如科技发展了,所有的 Car 都可以飞了,怎么办?有人说,很简单,给 Car 加一个 protected 的 fly() 方法,这样 Benz 和 BMW 就都可以飞了,继承真伟大!好,那么如果我需要建立另外一个 Car 的子类玩具车(Toycar), 我们知道玩具车可以 run, 但不能 fly 的。怎么办?还是好办,我们可以重载玩具车的 fly 方法,让他们什么都不干。那好,又一个子类来了,模型车(ModelCar)。模型车不能 run,不能 fly,好办,继续重载他们的这些方法。见下图 2:
图 2. Car 的第二个实现
如果我们有更多的 Car 的子类呢?有没有觉得有点繁琐,是的,我们需要重载太多的方法了。
继承并不能帮我们解决问题!
可不可以使用接口,我们可以把 fly 从超类中取出来,分别作为接口,Flyable,这样一来只有 Benz 和 BMW 才实现 Flyable 接口,ToyCar 和 modelCar 并不实现该接口。Run 也作类似处理。见下图 3:
图 3. Car 的第三个实现
大家可以看到,这其实是一个很笨的办法,除去 description() 方法,我们使用继承需要重载 3 个方法,可是我们使用接口实现则需要额外定义两个接口类和5个方法。接口方法里面并不能有实现代码,而 ToyCar 的 fly 行为和 Benz 的飞行行为也可能不尽相同,那么这就意味着我们需要实现越来越多的 fly() 方法。
接口也不行!
怎么办?想一想我们的前面提到的设计原则,把变化的和不变化的分离开来,以便我们以后可以轻易的改动和扩充,而不影响其它不需要变化的部分。我们变化的部分是什么?是否可以飞行,是否可以 run,以何种方式飞行?何种方式 run ? Benz,BMW 和 ToyCar 的飞行行为和 run 行为各不相同。我们可以把这些不同的 fly 和 run 抽象出来。见如下图 4:
图 4. Car 的第四个实现
看到这,也许您应该大概明白接下来应该怎么办了。是的,很简单,我们可以给 Car 类加入飞行行为和 run 行为的实例变量。而在初始化 Car 的子类时传入的具体行为进行初始化,这样每个子类就自然拥有了相应的行为。
代码参见如下:
清单 1. Car 的实现类
我们发现 CarComany 依赖的具体类有 8 个,如果任何一个类发生改变,CarCompany 都需要改变。这至少不符合我们的原则二:只和朋友交谈。应用我原则一,把变化的部分提出来。我们可以定义两个 CarCompany 的子类:AsiaCarCompany 和 EuropeCarCompany 用来生产不同样式的同一品牌的汽车。在这两个子类里面,需要做的就是生成不同品牌和样式的汽车,然后再调用超类的三个方法。这样的话我们可以把生成汽车的方法提出来。
清单 6. CarCompany 另一种实现
从这个图中我们可以看出:
Oh, My God! 这是什么?类爆炸?!好可怕的一件事。可以想象出来,这样的设计将来的维护成本又多高。假如我想增加新的配件怎么办,假如我想增加新的品牌又怎么办?
其实我们可以用实例变量和继承来重构上面的设计。见下图 8:
图 8. 第二个实现
我们在超类 Car 里面 cost() 方法计算各种配件的价格,然后在子类里面覆盖 cost() 方法,但是会调用超类 cost 方法得到配件价格和,然后再加上子类汽车的基本价格。
清单 7. Car 的另一种实现
参见我们的实现代码。
清单 8. Car 的另一种实现
结篇
设计原则不是统一的,不同人对有不同的设计原则有不同的见解,设计原则也不限于上面所陈述的几点。然后设计原则大的方向是统一的,那就是让代码尽可能的应对变化,尽可能的可复用。设计模式不是万能的,没有设计模式也不是不能的。然而在程序设计过程中遵循一些最基本的设计原则则是一个优秀的程序员所必需的,良好的设计原则的应用可以让您设计的程序从容应对可能的改变,可以让您的代码变得优雅而富有艺术性。
?
参考资料
学习
参考 设计模式概述,了解设计模式基本内容。?
?
原文:http://www.ibm.com/developerworks/cn/java/j-lo-beyondpattern/?ca=drs-tp4608
?