个人阅读+总结
No Silver Bullet – Essence and Accident in Software Engineering - Brooks / There Is a Silver Bullet – Brad J Cox
第一篇文章(Brooks)主要谈的是为什么软件工程不能像硬件一样,获得飞速的进步。然后作者提出了中心观点:软件工程中没有“银弹”,即万能的方法。作者先罗列了目前在软件工程中极大提高效率的进步,例如极大扩大的磁盘容量和飞速上涨的运算速度、高级编程语言、统一编程环境等等。但是这些技术无一能让软件的编写的效率得到指数级的增长。作者认为主要原因在于软件的四个特性:1. 复杂性(Complexity) 2. 服从性(Conformity) 3. 多变性(Changeability) 4. 不可见性(Invisibility) 。接着,作者提出了一些可能成为银弹的技术,例如 1. 更加高级的编程语言 2. 面向对象编程 3. 人工智能等。但是作者都对它们成为“银弹”持悲观态度。
我赞同 Brooks 的观点,我认为软件工程不存在银弹,原因不主要在软件本身,而在于我们对软件的需求上。
软件是现实的需求到计算机的投影。
我曾经看过知乎上有一个这样的回答,讲的是程序员出去买酱油(whatever)的流程,一个简单的打酱油,就用了无数个 if
else
来处理各种各样“异常情况”,比如超市酱油卖完了,比如服务员少找了钱等等。我们可以看到,我们现实中的需求有多么复杂。现实是多么复杂,它到计算机上的投影也不会简单到哪里去。软件,本质上就是编写一段程序,来使用我们的内存、硬盘和CPU(也许还有GPU、外设等等),以满足显示中的需求。这和单纯的制造物品是非常不同的。我们造一块肥皂,或者造一个电脑,都有明确的需求,超过了这个需求,不好意思,我们做不了。电脑的需求是简单的,它就是个图灵机,这个结构在一百年前已经被先知定了下来,人们为了实现性能良好的图灵机去不停研磨它,把它打造得更快、更好。但是如果你让电脑自己把屏幕的盖子打开,那就不是电脑应该完成的工作,我们拒绝这个需求。然而,软件面对情况不一样,软件生而用来解决问题,以及和问题相关的杂七杂八的问题。从某种意义上,我们只能诉诸软件才能去满足这些需求。例如,讨论区里一个简单的回帖的功能,就可以牵扯出一堆问题出来,这些问题跟计算需求没有关系,纯粹是现实的需求,比如说用户自己评论的帖子,可不可以删除,要不要加入点赞功能,要不要过滤敏感词,等等。然而,我们作为软件编写者,没有办法去拒绝这些需求,因为事实上也没有别人能来满足这些零碎而必要的需求。
软件的需求随时空而变化。
客户永远不知道他们的需求是什么。同时,随着时代的发展,老软件我们希望它能移植到新的系统上来,希望它能有新的功能,希望能优化它的一些子模块,等等。功能会过时,编程风格会过时,使用的语言会过时。软件总是要跟随时代发展,每一次维护都是艰辛的过程。我们看到代码就倾向于去重用它,因为写出正确的代码是开销非常巨大的事情。
当然,软件本身的复杂性也是巨大的障碍,来自计算机体系结构本身的复杂性我们也不得不考虑到。
即便高级语言对计算机的体系结构进行了很大程度的抽象,写出正确的程序依然不是简单的事情。操作系统把物理内存抽象成了虚拟内存,C/C++ 语言把虚拟内存抽象为了一个一维的数组,Java/Python 等高级语言更是不让程序员来管理对象的分配和释放,各种外设的操作只需要调用API,抽象程度这么高,为什么我们还是写不出正确的程序?
我们编写程序,要处理两个问题,一个是逻辑上的问题,一个是实现的问题。使用越高级(当然还要设计合理)的语言,我们就越多考虑逻辑上的问题,越少考虑实现上的问题。但是即使我们用 Python ,我们还是不可避免要犯错误,尽管 Python 的种种封装特性让 bug 不容易产生,即使产生了也比较容易定位。但是逻辑上的问题,不是那么容易考虑清楚的。我之前也提过,我们不管如何抽象这个问题,还是需要教计算机如何去计算,而不是告诉计算机它要算什么它就能自动地给你算出来,这个过程中就容易产生偏差。我们经常去假定一些东西是对的,凭借直觉写一些代码,或者没有经过充分的论证就写下条件语句,这些都是“弱智bug”的根源。而不管什么语言,我们都要考虑实现的问题,对于C/C++,实现的问题我们思考得更多,经常需要去手造轮子或者去查阅STL的资料,然后反过来削弱了我们对逻辑的思考。本质上我们编写Python也好编写C也好,还是脱离不开图灵机的一步步的状态转换,我们没法列一个方程(提一个问题)让计算机自动去解(别跟我提matlab),还是需要在内存上写写画画来解决问题,这从某种程度上是不符合我们人类解题的思路的。当我们面对一个方程组,我们通常不去一步步解它,而是观察它看有什么窍门,我们也不会去想什么中间变量来完成这一系列的运算。所以才会有高斯消元法这种适合计算机解方程组的算法,计算机本质上很难像我们一样去思考,人的思考和计算机的思考是不同调的。
关于面向对象编程。
There Is a Silver Bullet – Brad J Cox 的作者认为面向对象编程是银弹。但是面向对象编程在我看来也没那么好。
我觉得面向对象编程最大的优越性肯定是来自它的封装性。假如以C语言为例子,那我们可以认为每一个对象都是一个工程,因为一个对象就是一个命名空间,它有它自己的全局变量,同时它还可以有各种方便实用的接口,等等。当一个对象封装很好然后放上来,我们就可以把它当做一个黑盒。而且这样的黑盒是可以嵌套的,从而在不同层次工作的程序员就不需要关系黑盒里面的东西。
面向对象最大的优点,就是局部化。
“任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的Common Lisp实现。”(格林斯潘)
C 语言最大的缺点就是没有面向对象的功能,我们固然可以仿照C++的做法,建立一系列的编程规范,然后达到等效的封装,但是这根本就不如换一门面向对象的语言来得实在。况且,在语法上失去了局部化的特性后,我们还是无法预测是否所有成员都遵循这个编程规范,从而系统出现问题时,排查就非常困难,因为缺少concrete的东西来限制编程人员的行为。
面向对象没错,它很有用并且很流行,但是它依然解决不了所有的问题。
黑箱很好,黑箱内部在复杂的逻辑中还是丑陋的,黑箱也可以很大,里面存在复杂的逻辑行为,这些复杂的逻辑行为还是现实需求和计算机体系复杂性到软件上的体现。我们去保证黑箱一定不出现问题,依然不是简单的事情。同时,黑箱也把程序员割裂开,黑箱外面的程序员不需要理解黑箱里面、甚至懒于理解黑箱里面的东西。而事实上黑箱也很难在所有情况下做到完美的抽象,它里面的线头一旦暴露出来,就让人束手无策,一旦黑箱出现问题而无人维护,那就是很大的风险。
Big Ball of Mud - Brian Foote and Joseph Yoder
这篇文章讲的是我们的代码中可能出现一团烂泥球。我们去实现一个功能的时候,可能这时对系统理解还不明确,就瞎怼了一通,发现也确实能work,但是代码风格一塌糊涂,存在大段复制黏贴,命名古怪离奇,各个函数层次不明等问题,但是它work了,也没别的毛病,就在系统中保存了下来。当我们需要维护这个模块,就会发现它不可维护。
我总是想写出复用性好,阅读起来明晰的代码,但也不可否认我写了很多这样的“大泥球”。
拿编译课设中生成MIPS代码为例,加载数字到内存的时候,我需要考虑很多情况,比如加载一个char,还是一个int,还是常量,等等。之前没有考虑到各个指令存在的共性,就写了很多 if else
来在不同指令的生成中分别讨论这些情况,但后来发现,这些情况完全可以用同一个函数来封装它,这件事情完全可以写成一段可以复用的代码,然而这个时候我已经进行了大量的测试来保证原来的代码是正确的了,重写一遍的话会带来极大的风险,只好作罢。
减少泥球,长远来看,有利于项目的接手和维护,但是在某些时间紧急的情况下,靠泥球堆成的代码也无可厚非。事实上如果我们的程序员有时间的话,把自己写泥球整理成规范的代码,并不是特别困难的事情,但是要给出足够的时间返工和测试,重构一遍的代码很有可能复现不出当时考虑问题的完备性了。
The Cathedral and the Bazaar: Musings on Linux and Open Source by an Accidental Revolutionary - Eric S. Raymond
开源软件的开发模式有两种:一个是由一群限定的人去开发(教堂模式),在互联网上任何人都可以参与(集市模式)。
教堂模式的缺点是开发人员就那么几个,而把广大程序员隔离在外,它的项目了解的人就不够多,从而缺少测试;集市模式中,每个人都可以发起pull request,每个人可以参与到代码的编写中。
在我看来,两者各有优缺点,其实我更推崇集市的模式。
开源软件从本质上来讲就是人类的共同财富,无私的程序员不仅制造出来工具,还把代码贡献出来,自然是越多人来参与越好,每个人都可以在使用过程中发现问题就把问题改掉,或者增加新的功能丰富软件的应用等等。但是集市模式中鱼龙混杂也是重大的问题,由谁去accept这些pull request,按照何种标准去接受这些request,都是很困难的问题。同时当项目过大,修改的人可能根本不知道自己的修改是否不会整个系统的运作,或者造成代码冗余(不知道能复用的代码),从而让集市创造的软件真的变成了一锅粥,被越改越乱。但是如果我们能够选出对整个项目架构非常了解的人当评价pull request的人,这个人对整个系统都很了解,那么就可以尽量减少这些问题。
The Rise of "Worse is Better" - Richard Gabriel / Is Worse Really Better - Richard P. Gabriel
自由软件有两种编程哲学 “The MIT Approach” 和 "The New Jersey Approach",前者诞生了 Common Lisp 和 Schema,后者诞生了 Unix 和 C。前者是追求完美的编程哲学,认为界面一定要简单,实现尽量简单,但要为系统的完整性让路;后者是认为"更差就是更好"的编程哲学,认为实现简单是最重要的,其他的方面都要为实现的简单性让路。看起来,前者是更优秀的编程哲学,后者则有点后进生的习气,但现实是 Lisp 和 Schema 虽然有他们的功劳,但是现在已经不常用了;Unix 和 C 现在依然是非常常用的操作系统和最重要的语言之一。
事实上后者犹如曲线救国,看起来上来就输了半子,但是往往追求完美不如先去做个东西出来,然后把雪球越滚越大。C 就是很好的例子,C的基本编译器比较容易实现,从而可以在很多地方容易去移植,接受C的程序员就越来越多,C的社区就越来越大,反过来哺育了C的发展。C 一开始可能只实现了一半的功能,但是它确实能用,能提高工作效率,接着我们用 C 去实现更大的东西,完成更新换代。事实上软件工程就是对复杂性的妥协,完整性的追求可遇不可求,我们发布了worse是对它的妥协,但是我们的worse不是就保持worse的状态,我们测试它,使用它,提高它,去达到better才是最终的目的。
当然,worse is better有它的局限性。软件工程有它柔性、易改造的一面,也有它森严的架构不容侵犯的一面,因此打算了要worse的软件,往往更需要智慧的设计,让它达到better。为各种模块留下接口,有远见的预测要改善的地方,不要把代码写死,这都是往往需要架构师的精心架构,非一般人所能完成。
Managing the development of large software systems: concepts and techniques / http://www.youtube.com/watch?v=X1c2--sP3o0
瀑布模型(Waterfall Model) 是一个项目开发架构,开发过程是通过设计一系列阶段顺序展开的,从系统需求分析开始直到产品发布和维护,每个阶段都会产生循环反馈,因此,如果有信息未被覆盖或者发现了问题,那么最好 “返回”上一个阶段并进行适当的修改,项目开发进程从一个阶段“流动”到下一个阶段,这也是瀑布模型名称的由来。包括软件工程开发、企业项目开发、产品生产以及市场销售等构造瀑布模型。
瀑布模型是一种比较迟缓而稳重的软件开发方式,它最大的特点就是承认我们每个阶段都可能会产生问题,所以不断去回滚,去修复它。我觉得这在对可维护性、bug数要求比较高,但是对时间要求不是很紧的情况下,会取得非常好的效果。
Agile Method – by Martin Fowler / Agile is dead, long lives Agility / The corruption of Agile / In Defense of Agile - Nic Ferrier
敏捷开发是这样一种开发方式,系统被切割成若干个实现上可以相互独立的子系统,每个子系统分配给一些人,进行并行开发,并且保证各个模块开发完了以后都经过充分的测试,从而可以合成一个系统。
敏捷开发理想上肯定是一种很好的开发方式,但是实行过程中又会出现很多实际的问题。
设计周期长。
要将大型的工程,分割成若干个可以并行进行的小工程,并不是很容易的事情,最后得出的分割方案或多或少存在工程之间的依赖关系。同时架构的任务一般只能交给一个人去完成,前期准备时期会有无事可做的情况。
没有并行开发的合适工具。
拿这次软工团队的开发为例,我们的开发小组本来打算一人负责一块UI的实现,结果发现开发的平台不支持一个人修改一块 UI 然后通过 git 自动合并这些修改,最后我们没有办法,只好一个人改完另外一个人再改,完全就不敏捷了。
没有压力保证每个模块被充分测试。
虽然敏捷开发鼓励测试,但是实际上在并行开发中我们很难去有效地测试各个模块的功能,特别是工具不支持单元测试的情况下。有时即使在组员可以进行单元测试的条件下,由于时间等问题,测试也不够充分,从而导致对接的时候bug暴露出来,而且因为到处都是bug难以定位问题。
没有时间冲刺
小组成员如果有其它的事情,会导致开发时间不同步,一方做完了另一方还没有开始动手,造成无谓的等待。
Jez Humble: Why Software Development Methodologies Suck
文章认为,我们很难去衡量一个软件工程师的能力,各种测量的方式都有它偏颇的地方,而我们对方法的评价也很难基于客观的数据。我觉得确实是这样的,虽然软件工程方法其实都是经验之谈,但并非没有意义,至少一个团队按照某种方法去开发,总比各搞各的强很多。软件工程方法提高不了多少效率的上限,但是起码能稳住下限。虽然谈了这么多方法,我还是觉得开法人员本身的素质才是重要的,领导者的能力也是许多方法的成功运行所依赖的,与其追求花里胡哨的方法,不如静下心来去磨练技术,去改善人力的配置,这才是提高的关键所在。
posted @
2018-01-13 14:03
hitaku
阅读(
184 )
评论()
编辑
收藏
举报