记得十几岁的时候,受面向对象编程思想的影响颇深,凡事拿来一个对象,设计一堆属性方法,然后颇为自得。随着年龄的增长,维护的项目规模也越来越大,一堆对象和方法让我很头痛,一件事物,既可以归纳为A,也可以归纳为B,如何划分才能准确无误?在无数次的痛苦和纠结之后,突然发现,编程还是应该本位思考,事物就是事物,不能严格的用对象的思想来进行划分,数据就是数据,也没有办法使用一组简单的数据来描述一个事物。于是,重新开始使用面向过程的编程范式,直到STL和泛型编程。
Adobe公司首席科学家,STL之父 Alexander Stepanov 在一篇访谈中说:“OOP在技术上是有问题的,它妄图用基于单一类型的不同接口来分解世界,为了处理不同的实际问题,你需要不同种类的代数方法以横跨不同类型的接口族,我发现OOP在思想上是不健全的,它声称一切都是一个对象。即使真的是这样这也没什么意思,说一切都是对象跟什么都没说一样!”
关于OOP(面向对象的编程)之争已经持续了数十年,我并不是软件设计思想方面的专家,并不想争论OOP的优劣,我只作为一个资深程序来谈一谈如何减少项目中的问题,以及OOP对我们日常工作的影响。
当我们看到一堆文件和一堆重复方法的时候,我们会感到焦虑,尤其是在一个方法被封装、继承、重载之后,我手下的程序员往往会被这些事情折腾的晕头转向,在一段逻辑上反复纠结,所以我告诉他们,放弃OOP,禁止一层以上的继承,这仅仅是为了减低代码的复杂度。宁愿横向扩展内容,也不能纵向加深深度。在横向上面,我们很容易发现代码段的共性和问题,剩下的只是花时间修复,但在纵向上面产生的问题,则是牵一发而动全身,程序员不得不花大量的时间来保证这个类的设计完全合理,通常这很难做到。
我也并不是说面向对象就毫无意义,对模块和方法进行归纳整理还是可以的,比较好的方式是强内聚松耦合,这是老生常谈了。在面试的时候,大多数程序员都能轻松的回答出如此设计的好处,但真正理解,并能付诸实施的人却少之又少,原因大约有以下三点:
1、并没有真正理解这个设计方法和实现方式
2、项目时间进度所迫,来不及做好设计
3、这个模块其他人也改过,我实在是无能为力
常用的设计模式中MVC是很好的思想,将视图与业务逻辑分开,可以应对策划多变的需求,因此,在我的概念里,不管是何业务,View和Ctrl分开是基本要求,Model可有可无,人手充裕的话可以独立出来。但是偏偏Unity提供了足够的灵活性,在一个脚本中就可以做所有的事情,这就产生了实现的捷径,不用分层处理就可以实现功能,何必分层呢。在一个人的项目中,这是可以的,但在一个团队合作的项目中,这样就不行了,快速迭代,会让这样的结构手忙脚乱,bug频发。
那么,在项目中是不是应该严格的实现MVC呢?也并不是。现实中的业务千变万化,使用的引擎和框架各不相同,大家不要忘记,在各大厂商设计引擎和框架的时候,已经做了大量的抽象工作,使用了各种设计模式,在巨人的肩上再造一个巨人,难免会给人班门弄斧,画蛇添足的感觉。以我们使用的LuaFramework为例,其在Unity的基础上实现了一个MVP模式的框架,但其中的View层又并没有实际的业务逻辑来使用,硬生生的把tolua嫁接上去,其实其存在的意义并不大。其理想的状态应该是只给tolua提供增强的接口,所有逻辑,都用lua实现,以求最简化的结构和最优的性能,只是我们没有太多时间去整理这些代码而已。
对于第二个问题,任何的项目,都有时间进度压力,设计的本质是迭代,而不是固化思想。因此,从设计之初,就应该把迭代作为设计的重要因素之一,不能支持迭代改进的设计,压根就不是好的设计。为了支持迭代,将功能模块化就是必要的一步了,粒度越小,迭代成本越低,但通常这与代码编写速度相矛盾。为了快速测试,我们都习惯把相关的功能混在一起,不会有太多时间去区分其中的共性和特性。但我想说的是,这一部分的时间开销是必要的,它关系到你之后的维护成本。
我们正在进入软件工程快速迭代的时代,一个项目动则一年两年的日子已经一去不复返,为了满足快速开发的要求,加大人力配置是必然的手段,这就对项目组成员的协同配合提出了更高的要求。我的意见是,不要去修改其他人的代码,让他自己来。因为,你并不清楚他的考虑,贸然修改,只会造成错误。但如果他反复修改都不能满足使用要求,那就是能力的问题了。不同的人来修改同一个模块,势必造成冲突,这就是划分功能块的意义所在,通常情况下,我们希望一个功能模块稳定了,就不要再进行修改,即使修改,也是目的明确的优化,在不改变原有代码逻辑的情况下,扩展功能是可以的,改变算法是不可取的。基于这样的思想,程序员的责权意识就显得尤为重要了,如果一段代码前面,没有你的名字,你当然不用对这段代码负责,所以,我要求我的组员必须写上作者的名字(为了规避风险的项目除外)。所以,也不要让其他人改你的代码,除非你很清楚他在做什么!
最后,感谢你耐心看到了这里,为了满足强内聚、松耦合、模块化、快速迭代这些要求,忘记OOP是必要的,至少是让它在你的编程比重中不是那么大。只有基于这样的想法,你的模块才能足够的松散,减少相互之间的依赖,然后才能快速迭代。。。希望各位都能慢慢发现代码之美!