阅读一些关于软件开发本质和开发方法的文章的体会与心得

在本次软件工程课程当中,我已经经历了一次比较成功的个人项目,一次比较失败的结对编程项目,以及即将开始的团队项目alpha阶段。在这段时间,应教师的要求,我开始阅读一些有关软件开发本质和开发方法的文章,在此记录一些体会与心得。

文章一:

No Silver Bullet: Essence and Accidents of Software Engineering

by Frederick P. Brooks, Jr. 

文章网址: http://www.cs.umd.edu/class/spring2003/cmsc838p/General/NoSilverBullet.html

 本篇文章以杀死狼人的银弹为开篇,来探寻解决软件开销大的问题的方法。或者说,是使软件软件开发成本更低,软件开发更加便捷,以及在软件开发过程中避免一系列大小问题的方法。

就像作者所说,在软件开发领域并不容易找到这样一种解决软件开发过程中大大小小的问题的通法。作者首先分析在软件开发本质层面上分析了软件开发的困难之处 。软件,或者说程序,它的本质其实很简单,无非就是数据集及其之间的联系,算法以及函数调用。这一些抽象层面的本质,或者说是程序功能的上层代表,在不同的软件中都是相似的,就像我在面向对象建模技术课程中学习到的有关面向对象的抽象机制,它是上层而普遍的。尽管如此,软件开发的难题却并不在于构建这些概念模型以及抽象关系,而是在于软件开发中千奇百怪、各不相同的设计与具体实现,以及针对不同具体实现而进行的不同的具体测试。正是因为软件开发中具体实现的不确定性以及多样性,软件开发中就很难制定出一套通法来解决所有人的问题。

为了更清晰地描述软件工程本质上的难题,作者提取出了大多数软件系统中的四个无法回避的本质问题:复杂性,统一性,可变性以及不可见性。软件在开发过程中,往往由于复杂而多样的需求而具有不同的方面,每个方面还有很多不同的阶段和状态,对象的增多增加了程序逻辑的复杂性,同时对象之间的复杂关系也使得他们不能够被割裂开来看待,而是必须关联起来考虑,因此程序的规模并不随着对象的增多而线性增加,实际上的规模庞大让人们感觉难以处理。就比如上一次结对编程的电梯问题,看起来电梯就那么几种情况,就那么几个状态,只需要管理乘客的进出即可,十分简单易行。然而但是电梯内请求和电梯外楼层请求两种情况就弄得我焦头烂额。由于电梯运行过程以及乘客到达时间的不确定性,难以寻找到一种通用的最优方案能够解决各种乘客需求情况,于是也就经常出现程序对于随机的乘客要求处理很好而对上下班高峰时间的情况处理及其耗时的情况。情况的不确定性和易变性让程序员在确定了算法以及实现方案之后,调试起来才发现有太多的情况自己无法做到,即使考虑到了,有可能漏掉其他的情况,从而愈加让人苦恼。我们学习过线性代数,线性代数中利用向量和矩阵处理线性代数问题尚且那么复杂,而程序中逻辑与数据的关系更是难以用简单的线性模型来表示。在软件设计过程中不存在主要矛盾这一说,任何细枝末节的问题都必须解决,因为极限情况往往不是主要情况的模型能够解决的,就像对于一般情况适合的电梯算法却在极端情况下表现糟糕一样。这不同于数学和物理 ,只要抓住主要矛盾,忽略次要矛盾而抽取出复杂现象的概念模型就可以了。程序中一个地方的改动,往往引起其他模块的改动,因此还必须进行回归测试以保证之前的功能没有丧失。复杂的逻辑实现也让团队合作变得更加困难。在进行结对编程时,我和对方常常看不懂互相的代码,解释给对方都需要一段时间,有时候由于糟糕的设计以及注释的确实,连自己也看不太明白。理解的难题以及不确定性增加了程序的不可依赖性,你不知道它什么时候会跑出正确的结果,而什么时候会崩溃。我在编写电梯的过程中就是这样。第一版程序可以完成所有测试用例,时间性能也还不错。然后我进行了优化,发现了几个之前没有发现的小问题,改正后结果程序反而进入死循环无法跑出结果。有的时候一个输入数据明明满足条件,但是调试的时候程序就是不进入循环体。函数调用的复杂性增加了程序的时间复杂度,不合理的数据结构应用增加了程序的空间复杂度,进而让程序跑起来很慢,负担很重。有时候多人合作设计出了复杂的逻辑,考虑到了所有的情况,使用了特殊的数据结果以及在特定情况下才成立的算法,结果发现在日后需要对程序进行扩展的时候难以进行,推翻重来又不甘心,但是进行扩展势必更加复杂甚至出错,还有可能造成回归测试无法通过,带来更大损失。

在多人合作编程中,往往是不同人员负责不同的模块,然后最后进行集成。有的时候集成反而是整个项目过程中最让人头疼的部分。不同人的代码风格不同,制定的接口也有差异。如果说我要跟你对接,那么我需要利用你的对象暴露出来的接口,你也需要了解我的接口的作用。在这个“统一”的过程中,可能我因为需要适合你的接口而不得不修改我自身的程序实现,甚至修改我的模块本身的接口,这就导致你也可能修改你的程序以及你的接口,最终进入对接的恶性循环。因为适合不同人写的接口而进行的程序改动很可能是对程序有伤害的,甚至可能造成难以发现的隐藏错误,这些问题在后期的测试阶段难以发现,也更加难以改正。

不得不承认,世上万物都是可变的,没有绝对不变的食物存在。建筑会更改,电脑硬件会更新,但是都不如软件更新的频繁。软件发布之后,即便经过了单元测试以及内部测试修复了大部分问题,但是面对世界上不同的用户,软件开发者仍然可能每日收到大量的投诉以及修改建议,这些事情使得软件必须经常进行维护和更新 。例如手机端的qq和微信等软件,基本上过几个就要发布新版本集中修复几个或者几十个问题,而结果往往是用户对于新的界面不买账,或者是发现原有的一些功能不见了,也就是说用户体验还不如原来好,例如最近新浪微博的网页版更新了,而我却发现其中有一些以前有的功能现在居然不能用或者不好用了,这就会让软件开发者感到无所适从以及头疼不已。此外,硬件的更新以及使用平台的更改以及多样性也使得软件开发者不得不开发多平台的应用以及适应日新月异的硬件更改,例如苹果手机的硬件更新,微软系统的更新换代,都逼迫着软件开发者不断更改程序以适应新的需求。拿我的电梯项目距离,我估计下一次结对项目很有可能就是对这次电梯的设计提出新的要求,到时候又需要新的更改,势必也要进行回归测试,想想都很麻烦,等于是麻烦事永远没有终止,噩梦一次次重复地降临而你却无从避免。

在数学和物理的学习中,一个很重要的思想就是数形结合,或者说是图形化方法。图形往往更加直观形象便于理解。数学中的函数问题有时候可以借助画图得到迅速而简便的解决方案,物理中借助建模而体现的二维或者三维的研究模拟更是普遍,然而在软件开发过程中图形化方法却并不太好用。因为软件本身的逻辑并不太好映射到二维空间或者是三维空间,要想提取出图形模型就有些困难。有人可能会说,软件开发过程明明可以用到图形化方法,UML类图、UML程序流程图不都是例子吗?然而,这些图能够真正帮助你解决程序中的问题吗?类图表示类与类之间的关系,流程图表示调用关系以及运行流程,还不是需要用箭头来指向以及用符号来标注。即使你画出了图,复杂的情况和逻辑关系仍然需要思考并以程序的方式来表现,至少花UML图对于我编写程序没有什么大的帮助。有人可能会说,程序不是可以抽象出概念模型吗,这不是有助于开发么。我想说,概念模型抽象层次太高了,程序实现中太多细节难以在那个层次上表现出来,模块间的依赖关系,数据的流动与控制,有的时候复杂到即使你画出来图了也还是需要先进行部分分析然后才能进行整体分析。从而在软件开发过程中,形式化思维的作用也并不大,更重要的是对于性能的优化考虑以及对于多种情况的细节的处理。

即使软件开发从本质上来说就是复杂而困难没有通法的,人们仍然在积极地研究方法来使得软件开发变得更加方便、安全、高效以及低成本。在文章中,作者提到,高级程序设计语言,时间共享以及统一的环境都使得人们在进行软件开发的过程中更加方便高效以及安全。我对其中两点有一些体会。虽然我们学习的语言都是c语言、java语言等等高级语言,但是我们也曾经接触过汇编语言,明白这种较底层的语言使用起来比较复杂而且容易出错,最主要的原因就是太过繁琐低级。虽然低级语言运行效率较高,但是低级语言庞大的代码量以及难以维护的程序结构,使得我们在编写和调试的时候都感到十分头疼。而使用高级语言之后,很多底层的设计不再需要程序员考虑,而是由编译器和汇编器进行处理。这样我们就可以在高级语言层面上实现概念模型,加之不太耗时的调试就能够完成工作,这无疑是增加了软件开发的可靠性、降低其复杂性,尤其是现在很多的语言编译器本身自带强大的 静态或动态检查功能,甚至是大量的自动代码的生成 ,乃至现在各种各样代码框架的出现,例如java 的spring框架,ruby的rails框架等等,这些都使得我们的软件开发变得更加便捷快速而有安全性的保证。例如我本身对c#并不太了解, 但是由于vs的代码补全功能,我在输入一个类的时候就能看到许多的函数,或者说在引用自己写的类的时候可以直接选择方法或者成员,避免了程序的潜在性错误。同时,统一的编程环境以及大量库函数的实现与共享更是方便了人们运用已有的功能,进行代码复用,而不必花费大量的时间进行繁琐而使用广泛的函数的编写。由于提供了统一的接口,大家在开发完自己的针对具体平台的软件并发布之后,很快就能够被用户所使用,这也归功于定义好了的业界通用的各平台的固定接口,使得代码的集成和软件的使用变得更加方便而有保证。

虽然作者认为软件工程的复杂性是从软件开发的本质上就决定了的,但是他仍然抱有希望,列举了一系列他认为潜在的银弹,这些东西的出现已经对软件工程的复杂性有了一定的缓解作用,而且还可能进一步发挥他们的功效。首先就是高级语言,文中作者拿ada语言来举例。在作者看来,ada语言的贡献并不在于语言本身,而在于它所体现的现代化设计思想,例如模块化开发,建立抽象数据类型以及继承结构层次。这些设计思想可以说我都曾经实践体会过。模块化开发在大一学习C语言的时候就了解了。当时刚接触C语言时,所有的东西都写在main函数里,程序总共就一个函数,也用不着什么全局变量。后来逐渐开始把程序分割为一个个的模块,一开始还有些不习惯,要写那么多函数,有时候还需要使用全局变量和指针,比较麻烦。好处是在调试的时候显现出来的。调试的时候,明显感觉到调试模块化的程序更有针对性,调试的更快。同时模块化设计看起来也更加明了,像现在经常写的java和c#都是模块化设计了,不可能说把所有的内容都写在一个类里,一个方法里,那样不便于测试,别人也不容易理解你的代码。抽象数据类型是在面向对象建模技术课程中接触的。当时上课一直都在讲抽象,弄得我都烦了,现在c#、java等面向对象的程序写多了就感觉到,自己写程序的过程中经常在进行抽象,把现实的对象抽象为程序中的类,封装好成员变量和操作方法,然后就可以拿来用了,这样很轻易地就把复杂的问题划分开来,每一个类有他自己需要负责的部分,各个类之间通过信息传递来交互,使得程序的复杂性被划为对象以及他们之间的关系,而UML图无疑更加帮助开发者明确程序结构和类之间的联系,将具体复杂的问题抽象出来之后,站在更高的层面审视问题,往往能够发现一些关键。继承结构我倒是用的少,因为也没写过什么大型的程序,没有很多类需要从同一个父类继承而来,不过毫无疑问,继承层次是有利于代码重用的,同时体现了多态性,其实有点像接口和实现类之间的关系。说道接口,接口实际上就是抽象的结果,抽象出对象必要的成员和方法,规定好规格,便于他人调用,降低了程序集成时出现接口不对等的可能,从软件开发的根本上解决了一些复杂性和风险。事实上,很多程序员使用的很多语言都是使用面向对象的思想,包括我们现在平时做项目,也都是用的面向对象的语言。而抽象数据类型和继承层次,都是面向对象的基本思想,由此也可见面向对象编程在现代软件工程中的作用,它简化了很多东西,消除了不必要的麻烦,让程序员们在抽象层面,使用统一的接口进行交流沟通。

作者在文中也提到了可视化设计。这与我平时所做的图形化开发看似相同,其实有区别。我平时所做的图形化开发,比如在visual studio里拖动toolbox里的控件开发c#的图形界面,或者在Eclipse里拖动控件开发java的图形界面,这些是图形界面开发,在我看来大大地便利了我的图形界面开发。要知道,如果这些图形界面全靠代码来实现,要敲的代码会很多,而且有一些设置不在图形界面里看是不知道效果的。在图形界面开发中拖动一个控件就生成很多代码,方便而且不易出错。而作者在文中提到的可视化设计,更多地指利用图形来进行程序的前期设计,就比如老师让我们写程序之前先画好UML,我不知道其他人如何,至少我都是写完程序再画UML图的,作者在文中也提到了这个问题。图形化设计看似利用图形的直观性和形象性建立了模块之间的联系,能够高屋建瓴地设计程序,而实际上,在写程序的途中,我们经常会有或小或大范围的修改,有时候甚至推翻一个类或者一个工程重写,这些都是很常见的,也就是说程序本身的易变性和复杂性使得之前设计的流程图、类图等变得并不怎么有效,程序的诸多细节并不能表达在图上,否则图也将变得复杂,那样就达不到简化程序复杂性的目的了,反而需要花很多精力去理解图表示的联系和含义。

从面向对象建模课到软件工程课,老师都要求我们进行单元测试,面向对象建模课甚至要求我们写出类的正确性证明,确定不变式以及一系列输入输出的要求,以从抽象层面保证类和模块的正确性。在作者看来,这些测试或者说是正确性证明,并不能从根本上简化程序设计的复杂性。一方面是测试人员的负担很重,这我能够理解。我也做过几个程序的单元测试了,单元测试的用例设计就是比较头疼的问题,需要考虑到各个方面,而我们又无法拿到广大用户使用的反馈,就只能测试人员自己思考,难免会有疏漏;由于软件的复杂性,为其中数目庞大的类和方法编写测试程序也是一件很可怕的事情,测试程序有时候比源程序还要长很多。另一方面,正确性证明看起来从抽象层次,从源头上规范化一系列条件,避免程序出现错误,然而根据作者所说,软件工程最大的复杂性在于具体性,在于具体实现和具体用户的使用,这些具体的因素是不确定的,是多样化的。抽象的统一标准当然很难保证能够应对所有的具体情况。不是说你做了正确性证明,你的程序在具体使用中就不会出错了,具体的使用反馈往往让软件维护者们一次次地维护,让人伤透脑筋,同时还要做回归测试,这又加重了测试人员的负担,应对回归测试中可能出现的问题,看起来实际上是一个恶性循环,或者是一个无穷无尽的完善的耗费精力的过程。

 

文章二:

There Is a Silver Bullet

Big Ball of Mud

Brian Foote and Joseph Yoder

文章网址:http://www.laputan.org/mud/

在本文中,作者讨论了大泥球的含义,出现原因以及解决办法。大泥球实际上就是指一堆结构杂乱无章的代码。这些代码需要经常重复性的修补,并且在程序中信息共享混乱,不同系统组件之间的信息共享无组织无条理,导致很多数据都成为全局变量而存储,并且伴随大量的冗余信息。这种程序结构无疑会让程序越改越乱,最终陷入恶性循环,看不懂,不好改,改完之后更加难以理解,最终导致程序崩溃。这种结构的程序是为大部分有程序结构意识的人所鄙视的,但是这种结构的程序却很普遍,也就是说,很多有着良好意识的程序员依然会编出大泥球般杂乱的代码。

关于程序变为大泥球的原因,作者谈到,大泥球可能源于一次性代码。一次性代码,顾名思义就是指用完就扔,编完之后用一次就不再使用的代码。但是很多时候,我们需要做增量开发或者是想利用写过的代码,那么这些糟糕的一次性代码就被我们拿来或直接使用,或根据需求更改之后使用。由于是一次性代码,因此结构也非常糟糕,往往改了很多遍之后变得彻底无法理解,只能推翻重来,有的时候去理解这些被改过很多次的代码非常耗费精力,而且实际上是一种恶性循环。理解这些代码有时比重新写还要耗费精力。由于人的惰性,这些代码会一直被利用、更改,直到实在无法理解或者继续修改下去了,那就会进行小范围的重构甚至彻底的推翻重来。为了避免这些问题,我们一方面需要从一开始就构建良好的程序框架,明确清晰的程序结构,这样才有利于增量开发或者阅读理解。有的时候,需求变化的太快,高质量的代码也会被改的面目全非,这时候就需要我们时刻进行检查,检查代码的框架结构是否清晰,如果否的话需要进行重构以保证代码的整洁,这和可持续发展的思想是类似的。

高质量的代码一般是由高水平的程序员和设计者设计编写的,那么为什么高质量的代码也有可能变成大泥球?这实际上是一系列外在因素作用的结果。时间是一个很大的因素。时间包括有限的开发时间,紧张的测试时间和与其他人竞争的程序发布时间。就比如我的团队项目。项目本身有难点我承认,不过更难的是时间限制。团队项目的alpha阶段是四周时间,现在已经进入第三周,但是团队成员们都没有什么太大的进展,再想想程序集成、调试需要的时间,再加上一般人都有的拖延症,这个问题就变得比较严重了。由于有限的开发时间,我们很难选择最优的,甚至是合适的程序结构或者算法思想,同时有限的开发时间也使得开发人员不得不通宵达旦敲代码,就像我现在一样,导致开发人员的效率降低,影响到软件开发的可持续进行以及开发人员本身的健康问题。紧张的测试时间导致测试做的不够,程序中还有很多潜在bug没有修复。测试人员可能发现了代码的混乱,甚至预测到代码将成为一个大泥球,但由于有限的时间,难以重新构建或者修改程序结构,而这样势必导致更多的问题,想想我之前做结对项目的电梯,随便改几个函数,增加的调试时间可是以小时单位的,非常可怕。各方面的花费也是一个因素,这些花费表现在时间和酬劳等方面,也影响着开发者的积极性。至于开发者的经验以及技术实力,这就是开发者自身的问题了。虽然不是人人都有经验,但是搜索引擎非常发达,电子书实体书也是层出不穷,更不用说各大技术论坛了,其实关键还是看开发者有没有开发热情,愿不愿意学习或者花费精力拿出实力来构建高质量的程序。程序本身的规模、复杂性以及时刻变化的用户需求,同样让开发者们喘不过气来,容易让人自暴自弃,然后就应付了事交差。

大泥球的产生,表面上看来源于程序编写的不够好,是开发者的问题,实际上根本原因是外部因素。有什么样的环境,开发者就容易抱着对应的心态去进行开发。也就是说,我们想要从根本上解决大泥球的问题,还是要确定好外部条件,定好一些规则。比如开发中步骤的先后顺序,制定计划,写scrum meeting,画燃尽图,让开发者感受到重要性,明确自身的任务,明白当前的进度,团队内部需要多沟通,同时合理计划时间和分配任务,以使得任务能够更好地完成。显然很多团队都在抢项目,这个社会竞争太激烈,创意和好点子是要马上实现的,被抢先了的话很多努力虽不能说白费,但是换不回应有的收益了。不过比起完成一个低质量快速的项目,还不如好好做好项目本身,当然想要兼顾时间的话就需要适当的时间分配以及任务计划。其实,根源上的问题还是源于软件开始开发之前,或者说是有别于编程之外的那些必要的工作。如果说我们开发项目的时候,把实现功能,提早交付放在首位,那就很有可能使程序的质量趋近一次性程序,进而在后期的开发中导致程序逐渐变成一个大泥球。也就是说,设计构建程序逻辑应该放在首位,制定合理的框架和结构是必要的,有助于避免大泥球的产生,造福未来的增量开发和修改的工作。其实本身来说,编写一次性程序就是不负责任的表现,我们应该认真对待每一次任务,当成高质量的项目来对待,这样有助于养成规范开发的良好习惯,帮助自己的设计能力和编程能力的提高。

为了避免大泥球的产生,我们已经讨论了从根源上进行避免,同时我们也需要在开发的过程中时刻警惕大泥球的产生。我们需要定期检查程序的结构是否依然规整整洁,能够清晰地分辨出模块以及他们之间的联系,模块的耦合性是否过高,封装性是否完好等等。一旦发现问题,我们需要对程序进行小范围的重构,有的时候还需要对一个或者多个模块进行推翻重写。这些在程序的测试和维护中也是必要的。这些工作,与其说是防患于未然,不如说是以最小的代价解决问题,这些问题遗留到后面往往堆积成山,更难解决。

幸运的是,在我们的团队项目中,大泥球的征兆并不明显。因为我们做的是移植上一届学长的web程序到手机端上,实际上学长们硬性的把程序分成很多项目和很多类,就逼着我们也把程序以固定的框架和清晰的结构表示出来。当然在读了这篇关于大泥球的文章之后,我相信不仅是我,我们团队的成员们也会明白大泥球的可怕性,也会在编程和集成的过程中尽量避免。

 

文章四:

The Cathedral and the Bazaar

From Wikipedia, the free encyclopedia

文章网址:http://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar

这是一篇维基百科,讨论的是cathedral和bazaar两种软件开发模式。我在这里就把它翻译为大教堂和市集模式吧。说的更准确一些,这是开源软件的两种不同的开发模式。大教堂模式的含义是,源代码在软件发行后公开,但在软件的每个版本开发过程中是由一个专属的团队所控管的,例如linux下著名的编译器gcc。市集模式的含义是,源代码在开发过程中即在互联网上公开,供人检视及开发,例如linux之父linux开发linux核心的过程。其实我个人是比较倾向于市集模式的,因为这样无疑便利了软件的开发和测试。大家都可以对开发中的软件提出意见,也可以提问以加深自身对于程序的理解,以便今后使用;对于开发者来说,这样能轻易地收集到不少的用户反馈,从而影响开发者的开发方向,更加以用户为中心,满足用户的需求,同时发现很多以一人之力无法发现的错误。这实际上也是我所理解的开源软件的本质。既然开源了,程序都给别人看了,那为什么不利用一下开源对象这些资源来优化程序呢?

至于我们的团队项目,很遗憾我们目前是以大教堂的模式进行着开发,或者说,我们的项目都不算是一个开源项目,只不过是对于老师以及同学们开源罢了,并没有放到外网上。就算是老师和同学,内部也并没有以市集模式的形式开发,因为这样未免显得有些麻烦。自己的开发可能还没有进行完,还有很多地方需要修改,就这么贸然放出来看,怪不好意思的,或者怕丢面子,也不想展示给别人自己的真实进度,怕别人指指点点说自己拖延症严重或者太烂。总之我们还没有市集模式这个思想,所谓的市集模式顶多也只对团队内部成员实行。

 

文章五:

A Generation Lost in the Bazaar

Quality happens only when someone is responsible for it.

 

Poul-Henning Kamp

文章网址:http://queue.acm.org/detail.cfm?id=2349257

从标题直译过来就是写给在市集模式中迷失的一代,只有有人负责的时候,成果的品质才能体现出来。其实这篇文章就是作者在声讨市集模式而赞美大教堂模式,虽然这么说可能太直白了一点,但是我感觉文章表达的意思就是这样。作者首先批判了The Cathedral and the Bazaar (O'Reilly Media, 2001) 这本书,这本书也在大教堂模式和市集模式的wikipedia的条目中提到了,正是该书使得大量的IT从业人员转向以市集模式开发开源软件,也促成了开源软件的蓬勃发展。根据作者的描述,由于这本书的号召,大量的人们涌入IT行业,信心满满地进行着以市集模式开发开源软件,甚至使得IT行业的规模增加了两个数量级,而这两个数量级的人员中99%的人被作者认为是缺乏基本功的半吊子IT从业人员。他们既缺乏实践经验,又缺乏良好训练,以并不高超的水平在IT行业游走,而正是这些人增长了IT业界的泡沫。作者举了一系列例子来了说明市集模式的坏处,例如他自身的FreeBSD系统中的大量无用代码或者意义不大的代码以及混乱的代码结构,同时他也举例批评了unix中libtool和configure文件的无意义以及混乱。在作者看来,这些东西或者是本身编写的时候就有这些混乱而无用的结构,或者是由于市集模式的开源开发而被大量不明所以的水平不高的人乱改乱加,弄出各种各样的山寨版本来,导致人们视听混淆,以下恶性循环,越改越糟,以致本身高质量的开源项目被弄得面目全非。在作者看来,开源项目跟其他项目一样,需要有几个人全心负责,利用大教堂模式进行开发和维护,而不是像这样在市集模式下让任何人都可以乱动乱改。

其实市集模式的初衷是好的,集众人之力使程序变得更好,然而市集模式下开源软件却可能变得一团糟。有些地方都不知道被谁改的,改动的意义可能也不明确,不是每个人改了之后都会写文档的。改动地方的多少也不清楚,容易造成意想不到的错误,而改动者却不需为此负责,甚至可能不会再来参与这个项目。相比之下,大教堂模式的开发显得更加规范更加安全保险,团队内部负责并且沟通,同时也可以在软件发布之后征求用户反馈以便下一版做得更好。从这个层面上来说,大教堂模式才是软件工程的银弹,或者说相对于市集模式更能够减轻软件开发过程中可能出现的问题。其实讨论来讨论去,涉及到的还是软件工程中的关键问题,负责制度以及设计和维护的工作。软件工程需要工程化的操作,就需要规定以及规范,以及应对外界变化的能力。本身用户需求等多方面因素就已经够头疼了,如果还加上市集模式的外来干扰,那么程序的稳定性将受到影响,开发者的负担实际上将加重,甚至有可能导致开源软件形成大泥球,这是谁也不愿意看到的。其实,市集模式和大教堂模式都有其好处,都不能盲目支持,我们需要利用双方的好处来便利和完善我们软件工程的开发过程。

至于我们的团队项目,目前还没有遇到lost in catB的情况。我们就是以团队的形式进行开发,每天团队成员们都会进行实时的交流,并且大家都有一定的专业能力和学习能力,不会出现文中所讲的现象。我们的开发模式也没有想过由Cathedral变为bazaar,没有那么多纠结的成分。

 

文章六:

The Rise of ``Worse is Better''
By Richard Gabriel

文章网址:http://www.jwz.org/doc/worse-is-better.html

 这篇文章写得生动而幽默,但又不失专业的风范。文章开头比较了正确的做法和差一点的做法,并且举出了这两种做法的具体是实现的产物。正确的的设计风格的实现包括Common Lisp和CLOS,他们使用的设计方法被作者称为MIT approach,我把它译为麻省理工方法;而早期的unix和c语言利用的是差一点的做法,被作者称为New Jersey approach,我把它译为新泽西方法。看了在文章开头列的麻省理工方法和新泽西方法分别的含义,我依然感觉有些一头雾水,后来把全文看了之后,觉得实际上来说,麻省理工方法就是规规矩矩正确完备的做法,新泽西方法就是以实现的简单,以简单性为第一位的错误版的麻省理工方法,而可以牺牲部分正确性以换取简单性,或者更准确地说,以牺牲正确性来换取性能和实现的优化和简便性,不再考虑复杂的正确性的保证。初步看来,正确的做法肯定更加受人欢迎,因为新泽西方法有可能产生潜在的错误,这实际上是致命而危险的。但是事实却恰好相反。由于新泽西方法容易实现,对硬件的要求低,因此易于传播和移植,实现起来性能也不错,没有什么冗余的部分,以简洁明了而闻名于世。这是很容易理解的。使用麻省理工方法编写程序,需要耗费大量精力在正确性和接口一致性的保证上,而这些部分有可能是很少用到的,等于是花了大量的功夫在小概率功能上,因此编写起来费时费力,运行起来也需要比新泽西方法要求高得多的配置。然而,新泽西方法始终是牺牲了部分正确性,本质上的来说它是有缺陷的,作者把它称为是终极电脑病毒,因为这些东西实现和移植简便,对平台要求低,并且名气大,好用而使用数目庞大,因此也就为很多系统埋下了隐患。当然这些程序需要进行逐步改进,改进那些牺牲掉的正确性,逐步逼近麻省理工方法的正确性。然而新泽西方法始终无法避免潜在的问题,因此开发大型的复杂系统之时,有一些部分必须要用到麻省理工方法,也就是说正确性方法在大型复杂系统的开发上依然有用武之地,但是使用麻省理工方法开发依然非常复杂,加之还是大型系统,程序员的负担更加重了,甚至可能超出一些开发者的承受限度,因此差一点的新泽西方法依然是很多人的首选。

其实文中谈到的情况,就跟我们计算机学院的学生上编程课和自己编程一样。老师布置的作业,有的时候会规定很多东西,比如固定的接口,必须使用继承,写出正确性证明,给每个类写不变式和测试方法,写出抽象层次等等一系列保证正确性的要求,而我们真正实现的时候,要么就是懒得管这些,要么就是糊弄过去,弄一些形式化的东西。因为确实,认真弄过的人都知道,这些东西认真做起来,给自己增加的负担有时不亚于编写程序本身。然而不这么做,只图编写程序简便,程序没有经过正确性证明,虽然不说一定错吧,但是还是可能有一些隐患。这些东西也只能根据具体情况而定,或者是自身根据经验来选择了。

 

文章七:

Is Worse Really Better?

Richard P. Gabriel

文章网址:http://dreamsongs.com/Files/IsWorseReallyBetter.pdf

 这篇文章跟上一篇有着紧密的联系,作者都是同一人。在本文的开头,作者就提到自己曾经写过一篇关于worser is better的文章,并且比较了the right thing和worse is better。在这篇文章中,在我看来,作者其实是进一步肯定了worse is better设计模式的优越之处,除了便于实现和移植,性能优越,传播范围广之外,实际上worse is better不一定是错误的,因为他实际上是将正确性和准确性的优先级做了改变,做了不一样的权衡,换句话说,麻省理工方法将正确性作为第一要务未必就正确。作者在文中提到,很多用户或者企业偏爱进行增量开发,那么worse is better无疑更加适合进行增量开发和完善工作。同时新泽西方法性能优越,对机器的要求也低,传播范围更广的同时,能够让更多人知道用到它,进而能够得到更多的用户反馈,从而更好地进行修补和改进,不断提升正确性和性能。同时由于实现的简便性,更多的水平不太高的程序员能够使用新泽西方法来开发程序实现自己的想法,这也是新泽西方法的优点之一。同时,使用新泽西方法开发,受众更广,开发周期也更短,也更加易于维护。

作者在文中以C++为例说明worse is better的优越性。根据作者的叙述,c++被很多人称为“面向对象式的汇编语言”,这与c语言被称为”高级汇编语言“对应。而c和c++都采用了新泽西设计方法,即差一点的设计方法,它们因此有很多共同点,性能优越因而很多c程序员乐意转c++,并对c++的性能表示赞赏。同时c++提供的大量类库以及由于c语言的处理大型复杂程序逻辑的能力也让不少面向对象设计者颇为看好,作者甚至认为c++将成为未来面向对象的代表语言。且不说c++是否优秀,但是c++确实有着新泽西方法所带来的一些优点,诸如性能优秀,可移植性强等。不过就我个人看来,我更喜欢用java和c#,因为c++真的有点复杂,很多的用法容易搞错,而c++提供的类似c语言的让程序员操纵内存的方法也没有java和c#所具有的安全性。至于性能方面,听说现在JVM也做的不错了,性能并不算太差,而且我也没有开发过数据处理量很大的程序,因此对性能这一方面没有太深的体会。

 

文章八:

MANAGING THE DEVELOPMENT OF LARGE SOFTWARE SYSTEMS

Dr. Winston W. Royce

文章网址:http://www.cs.umd.edu/class/spring2003/cmsc838p/Process/waterfall.pdf

虽然文章起名为管理大型软件系统的开发,但是从url就可以看出实际上就是在讲述瀑布模型在软件工程中的应用。瀑布模型其实应该是根据形状来命名的,更加确切的命名我觉得应该是软件开发周期模型。在瀑布模型中,开发过程是通过设计一系列阶段顺序展开的。在每个阶段,从上一个阶段接收工作对象作为输入,利用这一输入实施该阶段应完成的内容给出该项阶段的工作成果,并作为输出传给下一个阶段,同时需要评判该阶段的实施,若确认正确,即通过正确性验证,则继续下一个阶段;否则返回前面,甚至更前面的阶段,也就是说会产生循环反馈,在当前阶段如果有发现了问题,那么将返回上一个阶段并进行适当的修改,项目开发进程从一个阶段“流动”到下一个阶段,因此被称为瀑布模型。

其实从软件工程的角度来说,瀑布模型核心思想是按工序将问题化简,将功能的实现与设计分开。利用数据库老师的话说,就是将逻辑实现与物理实现分开。将软件的开发分为多个阶段,并且规定了它们自上而下、相互衔接的固定次序,并且加上循环反馈。

瀑布模型有很多优点,例如可以分阶段每个阶段检查本阶段实现的正确性,避免把已有的错误带到下一层;把软件开发分成若干阶段,便于开发人员进行分工,并且只需关注自己的部分即可;同时瀑布模型等于是提供了一个软件开发流程的模板,我认为可以一定程度上避免程序变成大泥球,因为这个软件开发是有结构的,并且在每个阶段都进行自检,能够排除很多的当前问题和未来问题;同时,在增量开发的过程中,一般需要做单元测试,单元测试又需要做回归测试,而瀑布模型的循环反馈的工作就等于是做了测试的工作,更加保证程序的正确性,不放过任何一个模块中的问题。

 当然瀑布模型也有一些显而易见的缺点。例如分成阶段之后,每个阶段都需要做测试,测试不通过就不能进行下一个阶段,这样有可能减缓项目开发的速度,对于快速开发不太适用;同时,这种强制性也给程序员带来压力,上游的程序员必须尽早完成任务和测试,否则有可能被等待着的下游程序员所责难;还有一个潜在的问题,这是我在其他地方看到的,就是有可能在下游的测试中发现上游的问题,那么又要回溯到上游进行完善,然后一步一步走下来,这样是比较耗费时间和精力的。

 

文章九:

The New Methodology

Martin Fowler

文章网址:http://martinfowler.com/articles/newMethodology.html

 在本文中,作者讨论了敏捷开发的由来,敏捷开发与工程化方法的区别以及敏捷开发的不同风格。

作者首先探讨了敏捷开发的由来。现在很多的不成熟的软件开发都是边写边改,整个项目的开发过程没有一个明确而清晰的设计和计划,导致在后期测试的时候感觉永远测试不完,其实这就是我的切身体会。IT行业为了改变这一情况,引入了工程化方法,要求软件开发遵守严格的规则,写出详尽的文档以保证模块的正确性等等,但是显然这种方法收到了很多的抨击,主要因为它的繁琐和死板,并且无法根据变化的需求而灵活变化。敏捷性开发就位于边写边改和工程化方法之间。作者认为,敏捷性方法与工程化相比主要有两个特点。一方面,工程化方法是制定详细的计划,然后根据计划进行开发,是一种预见性的活动,而敏捷性适应用户需求的变化,是适应性的开发;另一方面,工程化是面向工程的,而敏捷性是面向人的。作者在接下来用两大块分别讨论了这两方面。

之所以提出工程化方法,是因为这种方法在诸如土木工程等许多实践工程中得到了应用,不过在软件工程这未必适用。建筑人员可以根据图纸直接进行建筑,而程序员却未必能够根据UML图等设计方案编写程序,因为在编写程序的过程中有可能发现很多设计中的错误以及没有考虑到的漏洞,也就是说程序员进行的并不是简单的建筑工作,实际上也是一种设计工作,那么软件开发过程中真正的建筑应该算是编译和运行了,因为只需按照编写好的程序就能够编译运行,将设计和建筑能够分离开来。这是因为,在建筑图纸的设计过程中,富有经验的设计人员可以根据数学公式和物理公式的严谨计算推导得出确切可靠的建筑所需值,而在软件设计中,即使是很有经验的设计者也难保在编码过程中不对设计提出错误或者漏洞,因为软件设计中没有数学公式可以进行严谨计算和验证。因此,软件设计计划的可预见性并不强。同时,用户需求在不断变化,时代也在不断演进,各方面的条件都在迅速变化,影响着软件自身所需要提供的功能,在这一点上来看软件设计的可预见性也不强。也就是说在软件工程的设计方面,做到可预见是很难的,因为一切都在变化,那么既然需求在变化,那么我们的设计可以转变成适应性的,即不断去适应需求,迭代产生根据需求而变化的新的软件系统,这也正是敏捷性开发做的事情。

 敏捷性开发面向人,以人为中心,主要表现在不把人看作是一个部件,而看做是一个复杂的非线性的个体,把程序员当做人来看待。以人为本的思想在很多方面可以得到体现。例如,管理人员不只是单纯地按照自己的想法给程序员分配任务,而是听取技术人员的一件,共同制定计划。同时,程序员需要有权利决定技术方面的修改和更新,也就是给予程序员更多的权利,让他们更多地参与到软件开发的计划制定中来,了解他们的需求,制定合适于人的计划,调用程序员的积极性,让他们切身体会到软件开发的设计计划和明确自身的角色和任务。以人为中心的适应性原则不仅体现在程序员身上,也体现在业务人员身上,主要表现为程序员应该多与业务人员进行沟通,通过业务人员了解当前外部的需求,以便技术人员更新技术或者改变计划,了解需求是进行设计和编码的必要条件。

理所当然,作者在接下来的文字中介绍了敏捷开发的一些方法。主要有XP(极限编程)、scrum、水晶系列开发方法(crystal)、相关环境驱动测试(Context Driven Testing)、精悍开发(Lean Development)、Rational Unified Process等。极限编程我们的团队项目中并没有用到,一是没有结对编程,而是我们对于软件开发各个环节的细抠程度以及所花费的时间甚至是设计的测试都远远达不到极限编程的要求。当然,极限编程是一种全新的体验,也是一种能够产生意想不到效果的体验。之前做结对编程项目时,我们就体会到了结对编程带来的意外收获,而结对编程也属于极限编程的要求之一。至于SCRUM,它的着重点是在软件开发的管理方面。scrum把一个项目分成若干个为期三十天的迭代阶段,每一阶段称之为一个sprint。每天有一个短会, 称之为一个scrum。对于scrum,我们团队在开发中应用了scrum meeting的方法,每天都有scrum meeting的发布,并且绘制燃尽图来表示我们团队当前的项目进度。至于水晶系列方法,它是一系列方法,有几个主要特征,包括经常交付、反思改进、渗透式交流、个人安全、焦点、与专家用户建立方便的联系、配有自动测试、配置管理和经常集成功能的技术环境,对于这一系列方法,我只能说我们的团队项目应用了其中的一部分方法,包括经常交付、反思改进、渗透式交流等。我们并没有使用Context Driven Testing和Lean Development。而至于RUP,其实RUP包含了许多内容,这些内容都是我们的团队项目中使用过的,包括迭代开发、以架构为中心(尽早设立一个架构以贯穿项目始终)、Use Case Driven(即开发是以用户可见的系统功能特征来驱动的)。

 

文章十:

文章网址:http://agile.dzone.com/articles/jez-humble-why-software

文章标题:

Jez Humble: Why Software Development Methodologies Suck

 文章中探讨了一些软件工程方法论不起太大作用的原因,也讨论了一些能够提升软件开发效率和提升软件价值的方法,方法有两条:划小开发周期以及提升反馈效率。在文中,作者谈到,软件开发方法论是一套方法,但是这套方法并不能够适应时刻变化的各种需求,也就是说一套方法论想要匹配所有的情况,想要在任何情况下都是用,是几乎不可能的。而为了证明方法论的可靠性,证明方法论是实际有效的,方法论的研究组织往往回去收集一些实际数据,而这些数据有很多都是从计算机科学专业学生进行的非正式试验或是从无法被有效控制的项目中收集的小量数据,因此这些研究组织的给出的论调基础往往是不健全的,数据缺乏分析,数据本身也缺乏代表性和说服力。因此不能断言某种设计模式就一定比另一种好,毕竟具体情况需要具体分析,有的时候引入一些想法,看似可行,实则可能无法适用。再说,就算软件开发的方法论在一定程度上是有效的,如果团队的学习能力和适应能力都不怎么样,那这个开发团队也难以发挥方法论的作用,难以践行方法论并作出良好的实践。

 回到文章开头,作者提到提升所提供软件价值的两种方法:划小开发周期以及提升反馈效率。在作者看来,很多人总是在纠结开发语言、开发框架、设计模式和方法论的选择,而并没有考虑最关键的因素,那就是开发者的能力。即使其他的东西制定的再好,没有有能力的开发者和讨论者,项目一样难以进行,制定好的东西没有人能够理解。而就算团队有这个意识,知道需要招募有实力的程序员,但是程序员水平的衡量标准又成为了 大问题。仅凭代码量和代码时间不足以全面准确地判断一个程序员的能力。由于软件开发环境和需求的不断变化,让程序员难以进行全面的测试,同时也很难判断是什么活动去起了解决问题的关键作用,也就是说,程序员水平的衡量标准并不是那么简单。同时,作者认为,缩短周期可以使用精益软件开发思想,有助于缩短开发周期。

总的来说,软件开发方法论之所以让人感觉鸡肋有几个原因,一是迅速变化的需求以及软件开发环境使得方法论无法适用于全部的情况;二是研究人员用来证明方法论有效性的那些测试数据往往是不具备代表性或者是缺乏说服力的;三是实际的项目很复杂,不同的应用环境应对了不同的处理办法,一种方法论难以完全包含;最后就是开发人员的学习能力和适应能力可能参差不齐,合作的能力和工作态度也各不一样,因此固定的软件工程方法论并不一定能够得到很好的执行。所以说,软件工程方法论有他自己的局限性,当然人员对于其的发挥实践表现也影响方法论的使用效果。

 评论中有很多人对作者的观点表示赞同。有的人赞成据作者的所有观点,比如方法论并不是普适性的,遇到具体情况需要具体分析;缩短开发周期或者将开发周期切分成小块有助于提高开发效率。有的人也提出了疑问,比如,他虽然赞同作者的观点,不过提出了一个问题,就是如何说服一个管理团队的人员,告诉他们增加工程参加的人数会起反效果。毕竟在项目人员的选择上,有时不仅是靠技术说话,谋略和策略也占有一席之地,想要说服有很大权力的管理层,让他们根据软件开发的规律来进行管理和设计,确实不是一件容易的事。还有的用户强调说,在软件开发的过程中,一系列的工具盒库以及框架等终究只是工具,人员才是财产,尤其是具有丰富经验的开发人员,更是不可多得。有些用户则提出了不同的看法。有一位用户提出,假设我们编写出的程序是符合物理公式的,那么我们可以经由物理公式的计算和推导来保证代码的正确性和有效性以及可靠性,但是可惜现在的代码并不是这样。现在我们所写的代码,杂乱无章没有清晰的结构,因此这位用户提出,写代码更多的是一种艺术的行为而不是科学的行为,如果说我们写出的程序都能够被公式所适合,那么我们离解决软件工程中一系列问题的日子就不远了。也有的用户认为,并不能只把人员当做软件开发的中心,代码本身也是很重要的,对于程序来说,代码就像是飞机的载重箱,是非常必要的,只不过需要有最优化而已,但并非不值得或者不需要关注。有些用户认为,方法论本身是好的而且有效的,只不过我们没有能够很好地理解方法论的内涵,一旦我们良好地利用、实现了方法论,那我们在软件工程中一定能够做得更好,避免更多的问题。

 

文章十一:

文章网址:http://continuousdelivery.com/2012/08/why-software-development-methodologies-suck/

文章和上一篇一样,不过发表在另一个地方,有另一些评论。

我们一起来看一下这里对于这篇文章的评论。

在这里的评论中,有人指出招募优秀人才,招募有经验的程序员进入项目组是很困难的,再加之用人单位对这些的重要性的不了解,软件工程以人为本的路还很长。有人用自己的亲身经历说明,自己经历了很长时间的基于软件工程方法论的开发才领悟到文中所说的道理;有的人准备去看看相关著作,他对文章中的论断表示惊讶,但是依然想去看看针对这一问题讨论的专门书籍,他也不愿意沉迷于偏见,希望自己去判断软件工程方法论的优劣;有些人在评论中说道,他们项目组进行过几次人员的重组,重组之后发现,共同工作一段时间之后,人与人之间所形成的那种合作的默契以及互相的合作模式不应该被打破,那种人员的归属感以及合作的感觉在频繁的换人和重组之后消失的无影无踪,这是需要避免的;有些用户提出,软件工程中以人为本是重要的,不过作为一个优秀的开发者,最可贵、最重要的是态度和责任感,其次才是技术,因为前者是后者换不来的,而没有后者可以由前者带来;一部分用户同意作者的观点,认为现在很多的软件企业过分关注方法论而不关注编程人员本身,在软件开发过程中所耗费的大量时间往往是在方法论上,因此留给开发人员编程的时间久显得有些不够了;有些人同意作者的观点,认为团队内部的沟通比工具的使用更加重要。还有人认为,或许方法论显得不那么有用,是因为方法论本身还不够完善,因此我们需要建立更加完善的数据结构,更加严谨的规定,考虑跟多的情况来使方法论发挥更大的作用;另一位用户提出,我们已经有了先进的理论和高效的工具,大部分人缺乏的是实践的力量,是将高效有用的虚拟化的规则和工具转化为有效的编程实践的能力。

posted @ 2014-10-26 10:54  hksskh  阅读(1093)  评论(4编辑  收藏  举报