前言
面向过程 还是 面向对象?这不仅仅是个软件工程术语,其问题甚至可以追溯到亚里士多德:您把这个世界视为过程还是对象? 回归到现今的软件行业,这不仅仅是个某个具体编程技术问题,更是认识论问题。
认识论 vs. 工具
UML创始人之一的Grady Booch在2004年IBM Developer Works Live大会的访谈中讲过的一段流传甚广的话。
我对面向对象编程的目标从来就不是复用。相反,对我来说,对象提供了一种处理复杂性问题的方式。这个问题可以追溯到亚里士多德:您把这个世界视为过程还是对象?在面向对象运动兴起之前,编程以过程为中心,例如结构化设计方法。然而,系统已经到达了超越其处理能力的复杂性极点。有了对象,我们能够通过提升抽象级别来构建更大的、更复杂的系统——我认为,这才是面向对象编程运动的真正胜利。
感想:
写过几年C++代码的我,一度以为所谓的OO其实就是继承、封装、多态、复用。复用这个词还是工作后才深入体会,大学期间老师传授的知识一直都是类、继承、封装、多态。无论怎样界定OO,他们说的都没错,只是不全面,只是OO的表象。OO编程的目标从来就不是复用,如果是失败的抽象,复用非但不会简化系统复杂性,还会令原本复杂的系统变得更加难以开发、维护。OO的精髓在于抽象,OO的困难也在于抽象,OO的成功在于成功的抽象,OO的失败在于失败的抽象,正所谓成也抽象,败也抽象。
软件行业,OO运动兴起之前一直以面向过程为核心。虽然我们今天都在讲OO,但是OO与PO之间无所谓对错,只是适不适合。万事万物都有其内部结构以及外部表现,软件开发亦是如此。以PO为核心,对内构造更为复杂的系统,对外解释越来越复杂的现实世界,已经日渐力不从心。PO无法胜任这样的问题,但是对于小规模系统架构,复杂度较低,PO的方法还是很管用的。而OO则是我们目前采用处理复杂性问题的方式,无论对内构造更为复杂的系统,还是对外解释复杂的现实世界,软件行业几十年的发展都证明了其正确性。为啥PO不行,OO就行,深层次上讲是我们认识论出了问题,不是工具的锅。
认识论 与 工具
何为认识论,通俗的讲是我们如何认是这个现实世界,面向过程 or 面向对象是我们采取的方法,这个问题自古有之。而我们今天在软件开发领域采用的众多技术,只是采用某种认识论时所使用的工具。这些工具或者技术,是前人软件开发过程中归纳和总结的最佳实践,掌握这些技术是重要的。掌握这些技术意味着你已继承前人经验积累,这是个捷径。但是这背后更重要的还是其隐含的各种认识论,即各种思想。工具会随着时间兴跌更替,但是思想不会。没有认识论就不会有百花齐放的工具,正所谓“皮之不存,毛将焉附”。
现实世界是怎样的?
Process-oriented
世界的一切都不是孤立的,它们相互地紧密联系在一起,缺一不可,互相影响,互相作用,并形成一个个具有严格因果律的小系统;而更多的小系统组成了更大的系统,所有小系统之间的联系也是紧密和不可分割的。
面向过程方法还认为每个小系统都有着明确的开始和明确的结束,开始和结束之间有着严谨的因果关系。只要我们将这个小系统中的每一个步骤和影响这个小系统走向的所有因素都分析出来,我们就能完全定义这个系统的行为。
将世界视为过程的这个方法本身蕴涵着一个前提假设,即这个过程是稳定的,这样我们才有分析的基础,所有的工作成果都依赖于对这个过程的步步分析。同时,这种步步分析的过程分析方法还导致另一个结果,即过程中的每一步都是预设好的,有着严谨的因果关系。只可惜我们这个世界从来都不是一成不变的,尤其到了信息化时代,一切都无时无刻不在发生着变化,系统所依赖的因果关系变得越来越脆弱。
Object-oriented
这个世界的本质是由对象组成的,平时看上去相互无关的独立对象在不同的驱动力和规则下体现出不同的运动过程,然后这些过程便展现出了我们这个生动的世界。相互独立的对象之间没有因果关系,平时是“鸡犬之声相闻,老死不相往来”的状态。只有在某个外部力量的驱动下,对象之间才会依据某种规律相互传递信息。这些交互构成了这个生动世界的一个“过程”。在没有外力的情况下,对象则保持着“静止”的状态。
每个对象都只与有限的其他对象有关系。分析对象时不再需要动辄把整个世界拉下水,从头到尾分析一遍,我们只需要关心与它有关系的那几个对象。这使得我们在分析对象的时候需要考虑的信息量大大减少,自然的,这就减化了我们所面对问题领域的复杂程度。
过程并非世界本源,过程是通过特定规则组织起来的一些对象“表现”出来的,原始的对象既独立于过程,也独立于组装规则。
Object-oriented的困难
OO的难点在于抽象,现实世界 到 对象世界间存在一道“鸿沟”,鸿沟的名字就叫做抽象
跨越这道鸿沟,我们需要:
- 一种把现实世界映射到对象世界的方法。
- 一种从对象世界描述现实世界的方法。
- 一种验证对象世界行为是否正确反映了现实世界的方法。
漫谈软件开发
简易的软件开发流程分为3个阶段:分析、设计、编码
最初的OO是从编程领域开始,标志是smalltack语言。诞生之初,其目的仅是改进开发效率,编写更容易管理、复用的代码。此时我们现今常见的继承、封装、多态的概念已出现。此时的开发流程如下图
此时只有编码是OO的,分析、设计还是采用PO的做法。这引发很严重的问题,就是设计如何指导编码,表现在:
- 编码需要的对象不能够从设计中自然的推导出来
- PO强调连续性、结构化、过程化;OO强调离散对象、事件驱动,两者间有难以调的矛盾。
Programmer在编码一侧对这种问题感觉尤为强烈,programmer不会想到是分析,还是分析到设计之间某个环节出现问题,只能认为是设计的锅。设计也只是针对分析,依据自己的认识论得出设计结果,被无脑背锅自己也委屈。此时整个软件开发过程中,设计最难受。为了解决设计难题,必须对症下药,编码那侧需要面向对象的设计,那采用OOD方法做出OOD设计不就可以了吗?现实也的确如此,为此出现了Booch86、GOOD(通用面向对象开发)、HOOD(层次化面向对象设计)、OOSE(面向对象结构设计)等OOD方法。
随着上述方法的普及运用,设计——开发间困难得到了解决。随着程序进一步复杂,分析的问题变得比设计更为突出,虽然设计——开发让我们写出漂亮的代码,但是最终实现功能却与用户需求大相径庭,导致整个项目推倒重来。不满足用户需求,再好的设计也是白扯。于是分析也采用OO的认识论,形成OOA。至此,整个软件开发流程全部采用OO的思想。虽然软件各个环节都采用OO的思想,但是各环节彼此之间由于其分工,专注领域的差异,沟通还是个问题。这就好比韩国人,日本人,中国人,大家说着不同的语言,同本国人(环节内部)交流没问题,但是要想彼此明白对方讲什么就要一种大家都认可的语言——英语。UML就充当软件开发中打通各个阶段,大家统一交流的“英语”。关于UML的作用,体会下面这个例子。
目前,随着软件工程的不断成熟,软件开发越来越朝着专业化和横向分工化发展。以前人们认为从需求 到 代码是一个紧密联系的过程,是不可分离的。一旦分开就会导致高成本和高技术风险。然而与现代工业的分工越来越细致和专业化的趋势一样,软件行业的需求、分析、设计、开发这些过程也被分离开来并专业化了。需求由专门的需求团队来做,甚至会委托给一个咨询公司;分析由专门的系统分析团队来做;设计由专门的设计团队来做……以往,开发人员是项目的中心,一个开发人员常常从需求一直做到编码:而现在,程序员只负责根据设计结果来编码,设计师只负责根据需求分析结果来设计,项目组里还有架构师、质量保证小组等许多角色,各自负担着自己的职责要求,在软件工程的约束下相互协作来完成一个项目。专业化、横线分工化的作用如下:
一家软件公司,没必要承担软件开发的全部环节,对于软件产业链上设计等关节环节自己把握,争取做精,低附加值的环节可以横向分工,最显著的例子就是软件外包。外包项目质量由少数甲方人员把控,大部分开发工作外包给乙方,乙方人员薪资要低于甲方。这样可以大大减低项目成本。
专业化、横向分工化并不是完全割裂的,两者有时候会穿插使用。以甲方Huawei、乙方China-soft举例,甲方将OceanStore 9000 CM这一个模块外包给乙方,乙方可以自己采集甲方需求,自己设计,自己编码开发。也可以甲方采集需求、设计,编码外包给乙方。还可以乙方承接分析-设计-编码整个环节,但是实际施工时再将某个环节二次外包。
从上面可以看出,软件开发各个阶段不仅是被拆分,更甚至由不同团队,不同公司来做。这里面沟通对接问题就会至关重要,UML正是为大家沟通对接建立了一套标准。只要按着这个标准,大家都能理解彼此的表打意图。