由“软件需要设计”想到的
由“软件需要设计”想到的
文中图片均来自网络
一个问题引发的思考
不久前,参加了一个OO训练营,在课上讲师问了这样一个问题,为何软件要设计?
这样一个开放话题引发了学员们的思考,他们思索的是“好的软件设计能为软件带来何种好处”,而我却被另一个问题着迷:我们是如何演进到软件需要设计?
关于我们是如何演进到软件需要设计,我想到这样一句话:
软件领域永恒的主题是如何做正确的事和如何正确的做事 ——《实例化需求》
如何做正确的事属于世界观,而如何正确的做事则是方法论,对我们中大多数人来说,方法论远比世界观要重要,因为那很可能会是我们谋生的手段。既然如此重要,那么如何做才算正确?让我们引用《重构》一书中Martin Fowler自己的描述来解答一位程序员的典型进化史:
初学编程的时候,我埋头就写程序,浑浑噩噩地进行开发。然而很快我便发现,事先做好设计可以让我节省返工的高昂成本。于是我很快加强这种“预先设计”风格。
业界大牛用他亲身经历告诉我们设计不是一天造就的而是经过我们自己的实践摸索或是别人的指点教导产生的。这个过程可能会是一个相当长的时间,我们会在这个很长的演进阶段里学习流程图、伪代码直至UML,利用上述设计工具让我们至少在内心建立起一个有边界、可操作的软件蓝图。现在设计也摆到开发人员手边了,是否就可以期待一个优雅的软件产品可以顺利地诞生了?
许多人都把设计看做软件开发的关键环节,而把编程看做只是机械式的低级劳动。他们认为设计就像画工程图而编码就像施工。但是你要知道,软件和技巧有着很大的差异:软件的可塑性更强,而且完全是思想产品。 —— 《重构》
很不幸,残酷现实无情地告诉我——不尽然!现实中随手拣起手边的代码就能发现充斥着大量冗长、晦涩、复杂的代码段,连我自己也不能幸免。这不禁引导我思考一个更深的问题,既然有了设计,为何我们还会写长代码?
情怀、教育、技术及现实
谈到长代码出现的缘由,下意识想到的竟然是武侠,也许程序员天生就是侠骨,深知行走江湖,“一寸长一寸强”的武侠真谛,如果软件行业比作江湖,那代码便是侠客手中的长剑,正所谓“长刀在手,天下我有”,代码越长挥舞起来就越是酣畅淋漓。在很多侠客看来,没法使用长剑的侠客终究只能是还未出师的学徒,而快意恩仇,仗剑天涯才是人生大境界,快哉!壮哉!也许正是这种侠客情怀让大多数人执着于此。
情怀之外,我们经历的软件教育似乎也对长代码起了推波助澜的作用。对一个刚学编程的人来说,阅读或编写一段长代码充满了挑战,而越短的代码越容易让初学者对编程有个大体印象,越容易让初学者模仿,也越容易让人起步,因此几乎所有编程语言的例子都是从只有若干行的hello world开始。随着时间的推移,特别是分支、循环的加入,程序体开始逐渐膨胀,开发者也希望摆脱初学者的卑微称号,于是所有人开始向着长代码进行,一夜之间长长的代码变成了区分初学者和高手的分界线,谁的代码越长,嵌套的分支、循环越多,水平就越高。我们的老师也是按照这样的流程来教导我们,更会鼓励我们去写长代码。从技能水平演进的角度出发,鼓励写长代码无疑是正确的,这对锻炼开发者了解代码整体结构和逻辑大有裨益,没有早期训练,就不可能产生大量可以从事软件工作的劳动力。
早期的编程语言中,子程序调用需要额外开销,这使得人们不太乐意使用小函数 —— 《重构》
这里又存在一个悖论,任何人似乎对过于长篇幅的事务,如会议、报告、说明书都会在早期就反应出不适应,这些反应包括:注意力不集中、困倦、抵触甚至拒绝,因而短篇幅从人的本能上更加受欢迎。不过技术上的考量有时候会成为决定因素,不少人使用冗长代码的原因是由于担心函数切换过程中产生的开销,特别是在那些对资源使用存在较大限制的场景中。如此小心翼翼地控制子程序的使用,实际上牺牲人的创造性而去提升机器的性能,因为人花在的创造性上的能量被阅读理解程序的上下文转换所占用,还有一部分要用于抑制由于长篇幅对自身机体造成的不良反应。
现实中的外部压力更是长代码出现的诱因,比如时间。时间是软件开发者最大的敌人,时间维度的约束导致开发者会采用照搬修改现有实现或者平铺直叙的方式来进行对抗,只要那样做是可以节约时间的。因此无论多么好的设计,只要时间压力足够大,就一定会有足够糟糕的实现。堆砌补丁是开发人员在面临巨大交付压力时通常会选择的方法,在这加一个条件判断,在那加一个循环,日积月累造就了“危楼高百尺”的长代码。
无论是情怀、教育、技术或现实,关键不在于识别开发者写下长代码是出于那种原因,而是如何为这些原因寻找一条出路,进而防止软件设计受到挑战。
出路
在探讨出路之前,先来看看一个简单的程序员进化史。如果从人类进化的角度考量程序员,那么大致可以把程序员分成4个阶段
- 原始人,即刚入门的初学者,一个方法或函数会囊括所有流程,一条main路通罗马,开发者通常在这个阶段停留的时间很短;
- 摩登原始人,会把流程拆分到几个函数中,但每个函数其实只是main的微缩,多数人会在此停留很长时间,这个阶段会给人一种错觉,那就是“你已经足够好,只有高手才能驾驭长代码”;
- 近代人,函数按照功能分解,表意清晰,适当采用设计模式,这个阶段是大部分人通过工作经验积累,特别是工作环境下的编码规范最终达到的,函数布局已经趋向合理,但仍会有长代码偶尔发生;
- 现代人,在近代人的基础上,增加了重构,并能敏锐地嗅探出代码的各种坏味道,这个阶段依靠开发者的自身修炼,通过读书、思考和实践,逐渐地找到消除长代码的合理方法
当进化到近代人,才算真正摆脱了小作坊式的代码生成模式,转变为大团队、多协作的工业生产模式,也即成为软件领域工作主力军。来自工作环境的约束将开发人员压制成为一个个标准件,标准件带来的好处是“臭味相投”:
- 大部分开发者会采用相似的编程手段、风格,对代码的理解、协作趋于一致
- 大部分开发者都会受到相似的外部压力,对于外部压力的外应也会趋于一致,甚至带有继承性和传递性
- 大部分开发者都会犯相似的问题,并且会被大部分人接受为传统或风格
长代码在“臭味相投”的环境中会得到发酵,由于得到了多数人的认同,长代码会逐渐成为团队允许的灰色地带,直到现代人的出现来破坏它。现代人以一种破坏者甚至征服者的姿态出现,他带来先进的思想及技术成为改变的关键,这一点从人类历史的演绎就能得到印证。
从公元1500年开始的这种技术和政治上的差异,是现代世界不平等的直接原因。使用钢铁武器的帝国能够征服或消灭使用石制和木制武器的部落。 —— 《枪炮、病菌与钢铁:人类社会的命运》
现代人拥有更先进的思想,因而他会质疑长代码的必要性和合理性,一旦他深入到长代码内部,真正领悟业务流程后,先前一切关于长代码出现合理的说法都会变成漏洞百出的谎言。他所有拥有的先进技术又会帮助他去斧正,不仅纠正代码,更会纠正一部分开发者的“三观”,进而推动更多的开发者转变为现代人,这种变化是可以被观察到的,并且是令人信服的。
进化始终都是事物唯一的出路,对生物如是,对技术亦如是。进化的结果便是适者生存,对于软件开发者而言,这便是能够适应这个需求快速变化的时代。既然大多数中的我们已经适应了从原始人到近代人的转变,那为何不再做点努力,让自己蜕变成现代人,而这将是我们摆脱长代码的唯一出路!