有个楚国人,坐船渡河时不慎把剑掉入河中。
他在船上用刀刻下记号,说:“这是我的剑掉下去的地方。”
当船停下时,他沿着记号跳入河中找剑,遍寻不获。
这就是刻舟求剑的故事。
听起来,这个楚国人太傻了。
这么明显的道理,居然想不明白。
然而,你笑别人的时候,其实别人也正在笑你。
要明白其中的道理,这还要从头说起。
软件,在外行人眼里,似乎很神秘。
他们看到的每一行代码,都是处于同等层次上的。
计算机只要把这些文本顺序过一遍,就能做相应的事情。
然而,在软件从业者看来,事情是复杂的。
这些文本之间是有层次关系的。
各行之前互相依赖, 牵一发而动全身。
那么,让我们随着软件规模的增长,来一探这些层次关系的建立过程吧。
我们将看到人们的认识,是随着软件规模的发展而逐渐丰富的。
没有任何技术手段,是凭空出现的。
这也提醒我们,不要过度使用不必要的技术。
技术水平应当契合软件的发展程度。
0-50行:美好的开端
如果,你实现了一个功能,写了50行代码。
那么,一屏幕就足以容纳它们了。
甚至,全部默写到另一个电脑上,也是可以的。
这里面可能包含了一些简单的控制结构,顺序,选择,循环。
所有的计算过程尽收眼底。
世界真美妙。
50-500行:提取代码片段
如果,你实现了一个功能,写了500行代码。
那么,一个文件就足以容纳它们了。
你可以把这个文件拷贝到另一个地方使用。
但是重新再写一遍代码,你是不想再做了。
随着规模的增长,函数出现的。
因为软件功能复杂了,你不得不在很多地方做相似的事情。
函数的出现,并不是为了追求花哨,而是不得已而为之。
函数式语言的世界或许并不如此。
一开始就有函数。
那么,也可以这样说,处理相同逻辑的代码片段被提取出来了。
500-2000行:隔离状态
如果,你实现了一个功能,写了2000行代码。
那么,一个文件夹就足以容纳它们了。
如果需要的话,你可以把文件夹打包发送。
对于重新建立文件,重新写代码,你都不想做了。
随着规模的增长,代码中出现了共享数据。
函数A和函数B要处理一段数据,但是函数C并不参与。
所以,把多个函数以及它们的共享数据放到一起,是个好主意。
函数式语言的世界里,这称为闭包。
纯函数式语言的世界里,状态隐含在顺序里面,于是出现了monad这种有趣的事物。
类,其实是跟原始面向对象无关的概念。
类是产生对象的其中一种手段,是一个模板,来生成对象。
除此之外,你或者还可以使用原型,来复制出一个对象。
继承,也提供了一种代码复用方案。
但是,不分情况的坚持使用类和继承,是自找麻烦。
当然,不分情况的使用任何东西,都是自找麻烦。
2000-5000行:规范化
如果,你实现了一个功能,写了5000行代码。
那么,可能就需要一个目录结构了。
这些目录中,有可能你用到了第三方的组件。
用到了同事或自己以前的代码库。
而且你明白了,可能你正在做的这个功能本身也只是一个中间产品。
你发现了一个高大上的概念,设计模式。
好像你不用设计模式,就无法跟别人进行交流一样。
于是,有些人膜拜它,有些人无视它。
然而,两个极端都不好,我们既要了解必要性,也要了解局限性。
设计模式降低了交流的成本。
设计模式还规范化了千奇百怪的设计。
我们避免重复阅读设计细节,也能为找到解决方案提供了思路。
但是,设计模式也不完美。
模式方案,可能不是最佳方案。
大量出现的模式,使代码千篇一律,增加了冗余度。
因此,在支持元编程的语言里,几乎很少提到模式。
但是,无视模式并不能表示你精通元编程。
这是两码事。
同时,代码清理和重构也会提到日程上来。
以及相关的一些提高代码质量的手段。例如,
于是,在这种规模的软件开发上,合作成了重要的话题。
软件项目管理,开始有用武之地了。
还是2000-5000行:世界观的转变
现在,你有了5000行代码,功能也实现了。
世界因此就美好了吗。
经验表明,事实不是如此啊。
每一次的需求变更,都会导致一定规模代码的改写。
当然灵活性,易扩展的软件架构会减小负担。
但也难免受到冲击。
随着业务的复杂化,如何定位问题,都已经成为问题了。
为了避免无止尽的重写,返工。
专门的需求分析,开始有人做了。
由于软件规模变大了,软件项目的参与者也增加了。
大家可能在同时修改代码,有的人也想看看昨天的代码是什么样的。
于是,引入了版本控制。
别人改了一处代码,结果影响了你的功能,这太常见了。
但是,怎样才能发现的早一些呢,最好在他改的时候,就告诉他。
单元测试可以帮助你。
目录结构中文件太多了,每一次集成,部署到生产环境,手动处理是很麻烦的。
如果能自动安装部署就好了。
自动化部署,也加上。
还有很多很多。
为什么会增加这么多额外的工作?
软件到底怎么了?
这些其实揭露了软件业不同于其他产业的一个显著特征。
那就是,软件是无时无刻不在变化中的。
软件是它生命期内,这个历史过程中的一个瞬间写照。
就像前文所说的刻舟求剑的例子一样,
我们应该用发展的眼光来看待软件开发。
这一事实,在软件规模足够大时,就越发明显起来了。
5000-10000行:自动化
可以说,计算机科学就是研究什么可以自动化的学科。
编程语言描述了自动化的处理过程。
自动化处理,让我们有迹可循。
在大规模软件项目中进行编码时,手动处理一切是不现实的,更是不适合的。
没有自动化的控制,我们几乎寸步难行。
我们不敢修改一行代码,因为我们无法知道后果是什么。
我们不敢修改一个类,因为它可能在成千上百个文件中都用到了。
我们不敢删除一个文件,因为它可能被某个模块动态调用。
那怎么办?
我们只能用软件本身来处理软件的问题了。
软件是用来处理复杂度的,那么当软件规模足够复杂时,
用软件来处理软件的复杂度,这种想法也是合情合理的。
所以,大规模软件项目,并不是一蹴而就的。
都有一个漫长的成长过程。
而伴随其成长的,是相关的辅助工具。
有第三方的合适工具可用的话,我们会尽量发挥软件的杠杆效应。
站在巨人的肩膀上。
如果没有的话,我们要有能力有意识的去制作工具。
人有别于其他动物的特点,不就是能制作工具吗?
我们还可以制作制作工具的工具。
这就是软件赋予我们的能力。
结语
软件是一个发展过程,
思想跟不上软件规模的发展,就会走得艰难,
而追求不切实际的技术手段,也是枉费力气。
软件的价值,在于存活期内它能为使用者带来多大效益。
这真是,春蚕到死丝方尽,蜡炬成灰泪始干呀。
就让我们把软件当做一路同行的知心朋友,
携手并肩,共同走向美好的未来吧。