昨天看到博客园的一篇新闻《程序员的回归式进化》,该新闻非常有意思,展示了同一段“Hello World”代码,一个1年编程经验、一个2年编程经验、一个3年编程经验、一个5年编程经验和一个10年编程经验的不同写法。这些写法中,让人啼笑皆非的是5年工作经验的程序员编写的代码看起来如此的复杂、如此的有技术含量,而10年工作经验的程序员编写的代码竟然和1年工作经验的程序员是一样的(除了注释,这是升华),真是有点讽刺,呵呵~~。
不过,笑过之后,还是让我忍不住陷入沉思。本人已经写了11年的C#代码,写了3年的Java代码,还使用过汇编语言、C语言、Delphi、PowerBuilder、C++、ASP、PHP、C++ Builder等写过不少代码,经历的项目和产品也不算少了。我一直在追求如何使软件开发变得更为简单,这种简单不仅仅是对于我自己,更是对于整个团队。
企业级软件天生就是复杂的,为什么呢?如果一个软件市场不错的话,那么这个软件总是会不断的进行演化,代码数量总是在不停的增加。以Spring为例,0.91版本发布时只有1万多点代码,1.0发布时有2万多代码,1.2发布时有4万多,2.0发布时有6万多,2.5发布时有7万行代码(以上数据参考《Java应用架构设计》),代码越多意味着越复杂。如果这些代码中,还存在技术债、设计腐化、循环依赖等各种内在的问题,那你想想看,如何使其回归简单?如果你加入一家公司,不幸的要去维护一个现有的系统,你如何回归简单?千万别告诉我,你要重新编写一套,这只是简单的技术思维而已!
回过头来,我们再看看那个5年经验的代码。其实该程序员的写法没有错,他已经学会了如何使用DI进行解耦合了,猜得不错的话,他也是学会了设计模式,甚至掌握了一些OOP的设计原则。但是如果整个应用系统都充斥了这样的代码,那我估计谁看谁发疯!
那么这个懂DI、懂设计模式、懂OOP设计原则的有经验的程序员犯啥错了?很简单,我觉得他什么错也没有犯,他只是依然缺乏经验而已。
抛开团队管理和协作层面,我们单从技术层面来考虑,想要使应用系统的开发变得简单,有效的方法就是化整为零,将复杂大系统拆分成小的、简单的、能够组合的“代码块”,简化系统开发并间接提高软件系统的可维护性和可扩展性。那问题是,这个“代码块”应该是怎样的粒度?下图是软件系统“自上而下”架构图(该图参照《Java应用架构设计》),服务/子系统的粒度大,类的粒度小,系统的展现层粒度一般比较大,而数据访问层粒度小。粒度越小,系统越抽象,越灵活;粒度越大,系统越简单,可复用性越低,扩展性越差。
想要使系统开发变得简单,选择“模块”作为开发、复用粒度较为合适,模块符合高内聚、低耦合,在模块内部,我们倾向于使用1年工作经验或者10年工作经验的代码,模块内部依赖关系较少,较为清晰,不宜在内部搞太多的抽象。在我们的实践过程中,模块内部不会使用IoC,也不太提倡使用太多抽象,会使用非常直接、简单的编码规则,使1年经验的开发人员能够编写模块业务逻辑。那么5年工作经验的那些代码适合在哪使用呢?
我的答案就是“模块间的边界”。通过对模块间的边界使用OOP的SOLID原则,能够更大提高模块的复用性和扩展性,同时还能够保持模块内部的简单,这或许就是在技术层面回归简单的一种较好的方法了!
企业级软件系统的复杂,不仅仅是技术层面,相反,管理与团队协作更容易影响系统的成败。一个复杂软件系统通常都是由团队协作来实现的,一个完整的团队通常有高级工程师、中级工程师和初级工程师,高级工程师负责架构和设计、中级工程师负责编码、初级工程师仅是入门。有效的解决三种层次工程师的协作,尽量减少交互、协作、学习的成本,能影响后续的维护成本。在我看来,良好的协作模型应该促使三者的工作更加的Focus,专注于自己开发的业务逻辑。同样的,模块化也能够很好解决三者的协作。一方面,通过模块化,我们可以消除软件架构(或者成为统一架构);另一方面,高级工程师负责框架类模块、中级工程师负责业务类模块、初级工程师快速入门协助开发,三者可以进行并行开发,而且代码互相隔离,互不影响。从这个角度看,初级工程师的入门速度也将极大提高。
因此,回归简单的一个好方法就是:(1)化整为零,将系统进行模块化划分;(2)在模块内部保持简单,使用1年或者10年经验代码,在模块间交互部分的代码使用5年经验的编码更是,应用OOP的SOLID、设计模式、SOA等抽象技术,提升模块的可复用行和可扩展性。