前两天和@大城小胖 就一个问题激烈辩论了很久。之后,我认真反思了一下造成我们观点有分歧的原因,由此,有了这篇文章。
事情从我和@flashlizi 讨论注释是否必要开始。在我开始编程的初期,我接触过很多质量非常烂的代码,没有注释,命名随意,奇技淫巧遍布 —— 不只是别人的代码,也有我自己几个月前写的。我深知代码缺乏可维护性,可读性是件多么可怕,多么让人讨厌的事。当时我能想到的解决方法就是注释。听到这么一句话“好的代码,注释应该占去三分之一的篇幅”,当时我很认同,在之后相当长的一段时间里,我也一直把这个当做是可维护性、可读性的最佳实践,甚至在我自己写的书里,我也将这句话记了上去,希望让更多的人能听到这句话。
直到有一天,我读了《代码整洁之道》(原名:《clean code》)这本书。书中的观点彻底颠覆了我对注释的信仰。书里提到"注释会随着功能的修改而慢慢腐坏,充满谎言",“只有代码本身才是最可信的”,“绝大部分的注释都可以通过好的命名或者开发工具例如svn来代替”,我很认同,就在我信奉注释的力量的几年里,注释其实并没有像我想像中那么强大。人无法回避的问题是人性,人不是机器,人有惰性,有时间压力,还有离职入职接手别人的代码,所以维护注释其实是非常需要过人的毅力,甚至道德的,“始终维护注释的高质量”这个要求甚至是反人类的。人们总会乐观地想“只要我坚持下去,只要我一直保持高度自律,就可以保证它不会腐坏”,事实上,这样的想法真的是过于乐观了。想想看,你有多少次强迫自己养成某个好习惯了 —— 晨跑?学英语?减肥?你坚持下来了吗?这就是人性,反人性的做法其实是很难坚持下来的,无论在初期你有多么强的信念。
想想吧,在编程领域也一样,定义好一个接口,让所有人继承这个接口,是不是比用文档向全组人员加一个“约定”要可信和安全?实现一个真正的私有,是不是比“约定”下划线代表私有更安全可信?在软件工程领域,早期人们是不是都非常认同瀑布?一切是那么严谨科学,一切按计划行事,风险该多小啊。但事实上,瀑布一直将人当做机器,无论是对工程师,还是对产品运营人员或者是客户,也忽略了人性。所以后来敏捷兴起了,无论是敏捷宣言,还是scrum亦或xp,都非常强调人性的问题。人性的力量不可忽视,能不依赖“人性的自觉”,不依赖“完美的计划,按部就班去执行”就尽量不要依赖。
回到主题上来,说说“架构设计”的思维方式,怎么样才是对的。小胖之前有几次跟我表达过这样的观点,我想到现在这仍然也是他心里一个未解开的结 —— 架构设计前期要设计好,不要因为架构没花心思设计造成后期在重构时需要大量的改动。对于这个问题,我想这句话要分成两个部分来理解,“架构设计前期要设计好”,“不要因为架构没花心思设计造成后期在重构时需要大量的改动”。后面这句话是我们要达成的目标,前面这句话是为达成这个目标而采取的方法。目标我认同,但方法值得再深究一下。
达到同一个目标“不要因为架构没花心思设计造成后期在重构时需要大量的改动”,可以有不同的方法:
1)前期花较大的精力进行全局考虑,将各个功能模块全考虑进来,不一定要非常细致,但应该全部考虑过,并在前期的架构设计里就将各个模块全部架进来。这里有个度的问题,如果设计得过于细致了,很有可能变成瀑布的开发方式,导致”过度设计“的风险很大。而如果设计得不是太细致,那倒是个可以考虑的架构方法。但设计一些不是太细致的模块,留在架构里占位,究竟值不值得按这个思路去做,其实还要从另一个角度去权衡——那些占位用的模块需要花费多大的精力投入,这些不够细致的模块会不会在接下来的开发过程中一直出现不同大小的bug影响其他模块的开发进度,这些占位用的模块优先级在什么位置,究竟要到什么时候才能有时间开始处理它们?如果优先级靠后,很多很花时间的功能完成之后才可能开始处理它,在前期就引进架构里,是不是价值不大或者弊大于利呢?
2)除了在前期就搭好占位符这个思路可以达到我们的“重构无需大修改”的目标,还有另一种方法——让模块高内聚低耦合,同时接口清晰明了,时刻保证各个模块处于一种“活跃”的状态,做好随时迎接变化的准备。打个比喻,如果第一种方法像个拳头很重,脚步很慢的拳击选手的话,它的致胜法宝来自于拳头的力量,有可能KO掉对手漂亮赢得胜利,而第二种方法就像一个拳头轻,但步法灵活的拳手,他没有一击必杀的能力,致胜法宝是时间到点数取胜。
我相信“简单就是力量”这句话,大部分工程师都听过。我对此的理解是“架构不宜考虑得太多太久远,如果未来两个月内都用不上,那就完全不考虑它了,当下的设计满足当下的需求”。但这句话只是一半,就像九阴真经有上下卷,武功有招式亦有心法一样,“简单就是力量”是对的,但还有一个必要条件“保持代码弹性,随时能够迎接修改,迎接重构”,只有做到了后者,前者才是对的。
简单就是力量,今天最好的设计明天不一定还是,到了需要修改的时候再修改,不用提前考虑太多,"把现在的代码做到灵活,随时迎接重构"要胜于"前期做好设计"。这是指导我编码、设计的心法,也是当下我最虔诚的信仰。
ps : to 小胖,某个类我不是不重构,只是还没到时候,而关于某些功能过于集中,应该拆开来,也不是我不拆,只是还没到时候。稍安勿躁,另外,请多一点信任。