面对对象的思考
在比较老一些的讨论面对对象方法的书籍中,都把继承看成了最为关键的部分。一般认为,面对对象的方法中最为核心的部分就是继承,因为从继承的角度思考问题,是和结构化设计最为明显的区别。如果,对象都没有继承,那么对象和传统的模块的概念没有太多的差别。我并不是要把这两种方法对立起来,我只是认为面对对象的方法背后的思想是应该和传统结构化的方法有所区别的。
在结构化的方法中,我们首先需要考虑的是整个软件系统的功能,然后按照一些基本的模块划分的原则,比如高内聚,松耦合,还有模块的扇入扇出数等等,把整个软件的功能分解为各个模块的子功能。当然,模块一般认为是黑箱的。在设计阶段需要定义的是各个模块的调用规则,我不想说是接口,接口我想用在下面另外一个地方。在完成了所有的模块的设计后,拼装好,就应该能够实现整个软件的功能了。当然这属于自顶向下的设计。这种思考的方式应该说是很自然的,就像搭积木一样,也和我们平时的思维习惯相一致,他不是探究为什么会有这样的功能,而是思考我如何实现这样的功能,当然我们的目标就是如何实现,但是他的思考方式是很直接的。这种方式有很大的局限性,一是需求分析总不是很明确,也永远不可能很明确,这就需要经常的变化原来的软件功能构想,这带来了麻烦,因为需要修改各个模块的设计,有时候甚至是致命的,会造成原来的设计被完全推翻。这需要一个很有经验的设计师,他必须了解这个软件适用行业的业务,必须了解未来的软件发展趋势,并且要能够预测用户可能的需求变化,这种要求是相当高的,一个设计师的水平就往往决定了一个软件的生存时间,就算他有一个很好的团队,他的拙劣设计仍然会使这个软件成为昙花一现。二是结构化设计带来的漂亮的文档,仍然不能为以后的维护工作带来相当的好处,大多数的维护工作都是由于用户的需求发生变化引起的,漂亮清晰的文档虽然能给后来的维护者很大的帮助,却仍然不能减少他们的工作量,维护者在改动的时候仍然需要全面准确的理解原来设计者的意图,否则就算有好的文档,维护工作仍然会带来大量的错误,特别是在用户需求发生大的变化的时候。在最近有了一些变化,我会在后面讨论。
面对对象的方法在开始的时候,第一个工作就是识别对象,识别在问题域出现的对象,最经典方式是寻找需求说明书中出现的名词,这是有道理的,语言是思维的外壳,这些名词在我们头脑中的概念决定了他们之间的联系,这种方式是偷懒的也是最自然的方式。第二步是寻找这些已经确定了的对象之间的共性,当然我们寻找的对象集应该是能够涵盖整个问题的了。寻找共性就是确定它们的共同祖先,在这个过程中设计师能够对问题有进一步的清晰的了解。第三步就是确定问题中各个对象之间的关系,也就是它们之间的消息传递,这不是指Windows中窗口的消息,呵呵。这个过程是苦难的,因为要确定各个对象之间的关系,也就是要更加精确的描述原来的问题,但是也是灵活的,就算理解出现了偏差,仍然是可以愉快的加以改正,因为我们是在理解对象,而不是分解功能,注意,我强调的是理解,而不是设计。这时候我们就可以提交我们的初步成果给客户使用了,有问题的时候加以修改,这种修改不会很困难,因为我们不是基于功能分解,而是基于理解问题域中的对象概念。我们不太会把所有的对象都理解错误,理解错的只是一个或者很少的一部分对象。我认为面对对象方法的实质在于迫使设计师从为什么的角度来设计软件,而不是很直接的怎么做,在这种理解过程中,很自然的形成了我们的设计方案。在这个机制中,能够起到关键性作用正是继承,他是连接为什么到怎么做的桥梁,他自然和不自觉的就把设计师对问题的理解变成了解决问题的方案。之所以会这样,那是因为,在现实世界中我们早就形成了解决这个问题的方法,我们需要的只是把这个解决方案转换成计算机的描述。我们在头脑中的各种概念就是我们解决现实世界的各种问题留下的痕迹,他为我们解决类似的问题提供了参考,请注意到一个事实,科学研究就是想知道是什么和为什么,而不是要干什么,但是我们知道了是什么和为什么以后,我们就会知道要干什么。举一个不恰当的例子,有一个工人很不满他的工资待遇,于是有一个人跑来跟他说,你这么少的工资待遇是因为我们国家还很困难,社会还在转形,不可避免的要有一部分人作出牺牲,于是这个工人就很满意了。为什么这个工人会满意?他原来要寻找的是解决他不满工资待遇的途径。其实,国家的概念在他的脑子中是神圣的,换一句话说,就是一种难以抗拒的力量,这是他的成长道路上教训形成的,当他不尊重国家的时候,老师会罚站,然后那个人的话,就演变成,你现在无法抗拒国家的力量,你现在这种状况是国家的意志,呵呵,这个工人知道怎么做了,就是安于现状。当然他不这样认为,他的概念是我是崇高的。这有点像宗教哦,有点跑题了。不过的确,理解一个问题,实际上就是寻找解决问题的途径。
在最近的一些日子里,传统的结构化的方法发生了变化,他开始吸收面对对象的概念,用于解决他的传统问题,就是修改模块困难的问题。一方面,他形成一些经典的设计模式,帮助提高设计师在设计同类问题时的能力,如同建筑中的经典设计结构,不过,这种帮助是有限的,我们很少见到一样的建筑物吧!另一方面,它使用接口的概念,意图就是,使模块的实现和描述进一步的分离,这种好处在于,可以在一开始就设计灵活性很高的接口,使用模块是通过接口,这样做实际上把总体设计的负担进一步减轻,他把责任推给了接口实现者,因为在结构化的方法中灵活和精确始终是矛盾的。很多人把这种变化看成是面对对象方法的延伸,而我始终坚持认为:面对对象的方法是寻找是什么和为什么的过程,结构化的方法永远是寻找做什么和怎么做的过程。