刻舟求剑

有个楚国人,坐船渡河时不慎把剑掉入河中。

他在船上用刀刻下记号,说:“这是我的剑掉下去的地方。”

当船停下时,他沿着记号跳入河中找剑,遍寻不获。


这就是刻舟求剑的故事。

听起来,这个楚国人太傻了。

这么明显的道理,居然想不明白。


然而,你笑别人的时候,其实别人也正在笑你。

要明白其中的道理,这还要从头说起。


软件,在外行人眼里,似乎很神秘。

他们看到的每一行代码,都是处于同等层次上的。

计算机只要把这些文本顺序过一遍,就能做相应的事情。


然而,在软件从业者看来,事情是复杂的。

这些文本之间是有层次关系的。

各行之前互相依赖, 牵一发而动全身。


那么,让我们随着软件规模的增长,来一探这些层次关系的建立过程吧。

我们将看到人们的认识,是随着软件规模的发展而逐渐丰富的。

没有任何技术手段,是凭空出现的。


这也提醒我们,不要过度使用不必要的技术。

技术水平应当契合软件的发展程度。

0-50行:美好的开端


如果,你实现了一个功能,写了50行代码。

那么,一屏幕就足以容纳它们了。

甚至,全部默写到另一个电脑上,也是可以的。


这里面可能包含了一些简单的控制结构,顺序,选择,循环。

所有的计算过程尽收眼底。

世界真美妙。

50-500行:提取代码片段


如果,你实现了一个功能,写了500行代码。

那么,一个文件就足以容纳它们了。


你可以把这个文件拷贝到另一个地方使用。

但是重新再写一遍代码,你是不想再做了。


随着规模的增长,函数出现的。

因为软件功能复杂了,你不得不在很多地方做相似的事情。

函数的出现,并不是为了追求花哨,而是不得已而为之。


函数式语言的世界或许并不如此。

一开始就有函数。

那么,也可以这样说,处理相同逻辑的代码片段被提取出来了。

500-2000行:隔离状态


如果,你实现了一个功能,写了2000行代码。

那么,一个文件夹就足以容纳它们了。


如果需要的话,你可以把文件夹打包发送。

对于重新建立文件,重新写代码,你都不想做了。


随着规模的增长,代码中出现了共享数据。

函数A和函数B要处理一段数据,但是函数C并不参与。

所以,把多个函数以及它们的共享数据放到一起,是个好主意。


封装和隔离,这就是面向对象程序设计的根本思想。

函数式语言的世界里,这称为闭包

纯函数式语言的世界里,状态隐含在顺序里面,于是出现了monad这种有趣的事物。


,其实是跟原始面向对象无关的概念。

类是产生对象的其中一种手段,是一个模板,来生成对象。

除此之外,你或者还可以使用原型,来复制出一个对象。


继承,也提供了一种代码复用方案。

但是,不分情况的坚持使用类和继承,是自找麻烦。

当然,不分情况的使用任何东西,都是自找麻烦。

2000-5000行:规范化


如果,你实现了一个功能,写了5000行代码。

那么,可能就需要一个目录结构了。


这些目录中,有可能你用到了第三方的组件。

用到了同事或自己以前的代码库。

而且你明白了,可能你正在做的这个功能本身也只是一个中间产品。


你发现了一个高大上的概念,设计模式

好像你不用设计模式,就无法跟别人进行交流一样。

于是,有些人膜拜它,有些人无视它。


然而,两个极端都不好,我们既要了解必要性,也要了解局限性。


设计模式降低了交流的成本。

设计模式还规范化了千奇百怪的设计。

我们避免重复阅读设计细节,也能为找到解决方案提供了思路。


但是,设计模式也不完美。

模式方案,可能不是最佳方案。

大量出现的模式,使代码千篇一律,增加了冗余度。


因此,在支持元编程的语言里,几乎很少提到模式。

但是,无视模式并不能表示你精通元编程。

这是两码事。


同时,代码清理和重构也会提到日程上来。

以及相关的一些提高代码质量的手段。例如,

结对编程代码审查测试驱动开发等,都耳熟能详了。


于是,在这种规模的软件开发上,合作成了重要的话题。

软件项目管理,开始有用武之地了。

还是2000-5000行:世界观的转变


现在,你有了5000行代码,功能也实现了。

世界因此就美好了吗。

经验表明,事实不是如此啊。


每一次的需求变更,都会导致一定规模代码的改写。

当然灵活性,易扩展的软件架构会减小负担。

但也难免受到冲击。


随着业务的复杂化,如何定位问题,都已经成为问题了。

为了避免无止尽的重写,返工。

专门的需求分析,开始有人做了。


由于软件规模变大了,软件项目的参与者也增加了。

大家可能在同时修改代码,有的人也想看看昨天的代码是什么样的。

于是,引入了版本控制


别人改了一处代码,结果影响了你的功能,这太常见了。

但是,怎样才能发现的早一些呢,最好在他改的时候,就告诉他。

单元测试可以帮助你。


目录结构中文件太多了,每一次集成,部署到生产环境,手动处理是很麻烦的。

如果能自动安装部署就好了。

自动化部署,也加上。


还有很多很多。


为什么会增加这么多额外的工作?

软件到底怎么了?


这些其实揭露了软件业不同于其他产业的一个显著特征。

那就是,软件是无时无刻不在变化中的。

软件是它生命期内,这个历史过程中的一个瞬间写照。


就像前文所说的刻舟求剑的例子一样,

我们应该用发展的眼光来看待软件开发。

这一事实,在软件规模足够大时,就越发明显起来了。

5000-10000行:自动化


可以说,计算机科学就是研究什么可以自动化的学科。

编程语言描述了自动化的处理过程。

自动化处理,让我们有迹可循。


在大规模软件项目中进行编码时,手动处理一切是不现实的,更是不适合的。

没有自动化的控制,我们几乎寸步难行。


我们不敢修改一行代码,因为我们无法知道后果是什么。

我们不敢修改一个类,因为它可能在成千上百个文件中都用到了。

我们不敢删除一个文件,因为它可能被某个模块动态调用。


那怎么办?

我们只能用软件本身来处理软件的问题了。

软件是用来处理复杂度的,那么当软件规模足够复杂时,

用软件来处理软件的复杂度,这种想法也是合情合理的。


所以,大规模软件项目,并不是一蹴而就的。

都有一个漫长的成长过程。

而伴随其成长的,是相关的辅助工具。


有第三方的合适工具可用的话,我们会尽量发挥软件的杠杆效应。

站在巨人的肩膀上。

如果没有的话,我们要有能力有意识的去制作工具。


人有别于其他动物的特点,不就是能制作工具吗?

我们还可以制作制作工具的工具。


这就是软件赋予我们的能力。

结语

软件是一个发展过程,

思想跟不上软件规模的发展,就会走得艰难,

而追求不切实际的技术手段,也是枉费力气。


软件的价值,在于存活期内它能为使用者带来多大效益。

这真是,春蚕到死丝方尽,蜡炬成灰泪始干呀。


就让我们把软件当做一路同行的知心朋友,

携手并肩,共同走向美好的未来吧。