你以为你了解面向对象设计么?
你以为你了解面向对象设计么?你可能说:“是的,我了解!”那么好,来看一个例子。
下面这个设计,你认为是面向对象设计么?
Button类的代码如下:
public class Button
{
private Lamp lamp;
public void Poll()
{
if (/* some condition*/)
{
lamp.TurnOn();
}
}
}
你可能会说,这个设计用到了封装、继承和多态,这难道不是面向对象的三大机制么?
那么好,你的回答说明你已经很了解面向对象的基础知识了。但是这就是面向对象设计了么?
百度百科里关于面向对象设计是这么定义的:“面向对象设计模式是‘好的面向对象设计’。”刚开始看到这句话,我怀疑这句话是不是有语病。其实,它的言外之意是:差的面向对象设计不是面向对象设计,所谓“好的面向对象设计”是那些可以满足“应对变化,提高复用”的设计。
上面这个设计严格的说,有不好的、或不成熟的地方。主要是Button依赖Lamp类,这主要会有2个问题,一是Lamp改变,Button类会受影响,即所谓的僵化性。二是想要重用Button类去控制其它的东西是不可能的,即所谓的顽固性。这个例子是《敏捷软件开发——原则、模式与实践(C#版)》一书“DIP:依赖倒置原则”一章中的例子,笔者稍加修改。
运用依赖倒置原则把Button和Lamp的关系改进后的设计是这样的(见下)。
至于依赖倒置原则,说的已经太多了,不是本文重点,略过。但Bob大叔的一段话,还是很值得琢磨的。
“事实上,这种依赖关系的倒置正是好的面向对象设计的标志所在。使用何种语言来编写程序是无关紧要的。如果程序的依赖关系是倒置的,它就是面向对象的设计。如果程序的依赖关系不是倒置的,它就是过程化的设计。”
之所以写这篇随笔,是因为最近看另一篇关于面向对象的博客“我们需要养成面向对象的编程习惯”及其评论时感到,有些朋友把面向对象设计和非面向对象设计的对比等同于面向对象编程语言(OOPL)和非面向对象编程语言的对比。
引用百度百科“面向对象设计”词条中的一些话来阐述一下:
通过面向对象编程语言(OOPL)认识到的面向对象,并不是面向对象的全部,甚至只是浅陋的面向对象。
OOPL的三大机制“封装、继承、多态”可以表达面向对象的所有概念,但这三大机制本身并没有刻画出面向对象的核心精神。换言之,既可以用这三大机制做出“好的面向对象设计”,也可以用这三大机制做出“差的面向对象设计”。不是使用了面向对象的语言(例如C#),就实现了面向对象的设计与开发!因此我们不能依赖编程语言的面向对象机制,来掌握面向对象。
本来可以结束本文了,但是考虑到有些朋友可能会置疑前文中的一点,所以再补补漏。
有些朋友可能会认为,如果Button和Lamp一旦设计好后就不会再变化,那么最前面的设计可能就是一个好的设计了,而后面的设计反倒有不必要的复杂性,或者说是《重构》中所说的“夸夸其谈未来性Speculative Generality”。
还有朋友可能说,敏捷的设计是用增量的方式来完成的,敏捷的技术实践TDD、重构都是主张先做足够用的设计,然后小步持续改进。所以最前面的设计如果是够用的,那么就没有必要一开始就把它弄复杂。
以上说法,本人也很赞同。本来世界上绝对的东西就很少,尤其技术领域。毕竟,我们智慧的价值,就在于能寻找预见性和适应性的最佳平衡点,这不是机器能做到的。