在第十三次做业中,咱们使用了JUnit单元测试框架对咱们在第三次做业中编写的捎带电梯程序中的每个方法进行了测试。捎带电梯程序在通过了互测以后,已是一个比较完善的系统,可是经过大量细致的单元测试,仍能从程序中寻找到一些逻辑漏洞甚至错误。经过观察每一个方法的语句和分支覆盖率,能够比较直观地看到本身的代码是否每一行都有其价值。在写第十三次做业的过程当中,我不止一次遇到了这样的状况:在针对某一个复杂的方法设计大量测试用例,甚至用程序自动生成随机测试样例,都没法对某一条语句或者某一个分支作到100%的覆盖。这个时候,我就会从新检查这个方法的实现逻辑,判断这条语句或者这个分支是否真的有存在的必要。很显然,细致的单元测试有助于咱们优化和完善本身的代码。算法
而在第十四次做业中,咱们采起了正确性论证的方法对同一个捎带电梯程序进行了正确性分析。经过对JSF的后置条件进行划分,将一个方法所须要处理的全部状况分为几种小的分支,而后对这些小分支逐一审视代码的实现是否知足了JSF的后置条件。在JSF后置条件明确且可验证的基础上,正确性论证可使咱们从新审视本身的代码,细致地分析其实现逻辑,并确认本身是否覆盖了每一种可能的状况。因为第十四次做业是在第十三次做业完善后的程序之上完成的,所以正确性论证没有帮助我找到代码中的逻辑漏洞;但它驱使我完善本身的JSF后置条件,而且对规模较大的方法进行拆解或重构,以达到可进行正确性论证的目的。从这个角度上来说,正确性论证进一步优化了程序的可验证性。编程
单元测试和正确性论证的本质差别其实用一句话就能够归纳:单元测试是黑盒测试,而正确性论证是白盒测试。 安全
在构造单元测试的测试样例时,测试者能够彻底不知道程序的具体实现细节,机械化地生成测试数据,并根据方法的规格判断方法的实现是否正确。它的好处是简单粗暴快速有效,通过大量测试数据的轰炸,代码中最细微的漏洞也能够被揪出来并得以修正。此外,单元测试对代码的重构具备奇效。若是没有自动化的单元测试,重构代码的时候就须要不停地去手动验证以前的测试样例,一旦发现问题,修改以后又要重头跑,既浪费时间,又容易出错。单元测试有助于程序设计者以自动化的方式去验证程序的正确性,这是正确性论证所不具有的优势。可是,单元测试也有其缺陷:因为它是黑盒测试,从几率的角度上讲,不管测试数据再怎么完善,总会有漏网之鱼的Bug出现。换句话说,在程序有一个良好的设计且规格完善的前提下,单元测试能够保证程序的正确率达到99%以上,但没法确保100%正确。多线程
正确性论证与单元测试不一样,它是白盒测试,这意味着测试者须要深刻代码的实现,去逐个论证明现与规格上的不一样之处。这个过程比较繁琐,并且对重构极不友好,一旦代码发生较大的变更,许多方法的正确性论证就要重写,形成不少额外的麻烦。可是,这些麻烦换来的好处就是,一个被正确设计了的且规格完善的程序只要完成了正确性论证,就能够从逻辑上确保100%的实现正确性,其保证力度要大于单元测试的保证力度。所以,对于一些再也不须要进行修改的方法,进行正确性论证是比单元测试更为稳妥的选择。框架
在实际的工程中,我认为应以单元测试为主,正确性论证为辅。由于需求老是在不断变更的,单元测试的简便和高效适合更现代软件工程的开发流程。而对于那些比较重要的核心算法代码(即不会随需求更改而变更的代码),应进行正确性论证,以确保逻辑上的100%准确。工具
值得注意的是,不管是单元测试仍是正确性论证,其有效的前提都是程序的设计是合理且正确的,各个类和方法的规格也都已经完善。没有规格的代码就像没有标准答案的考试卷,学生答起来群魔乱舞,老师判起来无从下手。单元测试和正确性论证都是必要的,但这并不表明有了它们就能够万事大吉。在软件开发环节的最开始就作出合理的设计,而且撰写好相应的规格,单元测试和正确性论证才能发挥其最大做用,为提升程序的正确性带来价值。性能
OCL语言是约束(Constriant)语言和查询(Query)语言。一个约束就是对一个(或部分)面向对象模型或者系统的一个或者一些值的限制,UML类图中的全部值均可以被约束,而表达这些约束的方法就是OCL语言。在UML2标准中,OCL语言不只可以用来写约束,还可以用来对UML类图中的任何元素写表达式,每一个OCL表达式都能指出系统中的一个值或者对象。由于OCL表达式可以求出一个系统中的任何值或者值的集合,所以它具备了和SQL一样的能力,于是OCL也是一种查询语言。单元测试
OCL语言的基础是数学中的集合论和谓词逻辑,而且它有一个形式化的数学语义,可是它并无使用某种数学符号。由于虽然数学符号可以清晰的、无歧义的表达事物,可是只有极少的专家能够看懂。因此数学符号并不适合用于一个普遍应用的标准语言。天然语言是最易懂的,但它倒是含混不清晰的。OCL取了天然语言和数学符号的折中方案,使用普通的ASCII字符来表达数学中一样的概念。学习
OCL是一个类型语言,任何表达式的值都是属于一个类型的。这个类型能够是预约义的标准类型例如Boolean或者Integer,也能够是UML图中的元素例如对象。也能够是这些元素组成的集合,例如对象的集合、包、有序集合等等。测试
OCL是一种声明式(Declarative)语言,表达式仅仅描述了应该去作"什么",而不是应该"怎样"去作。由于OCL是声明式语言,因此UML中的表达式被提高到了纯建模的领域,而没必要理会实现的细节和实现的语言。
OCL起源于1997年BIM公司为响应OMG的"面向对象分析和设计标准"征求稿所提交的"对象时间限制提议",OCL是该提议的部份内容。用OCL能够描述四类约束,分别是不变量、前置条件、后置条件和监护条件:
1)不变量是在属性的生命期内一直保持为真的规则。
2)前置条件是在一个操做被调用时必须为真的约束。它是一个断言,不是可执行语句。
3)后置条件就是在操做完成时必须为真的约束。它不是可执行语句而是断言,必须为真。
4)监护规则是在对象可以从一种状态转变为另外一种状态前其值必须为真的约束。
每个OCL表达式都必须赋予一个明确的上下文来定义参考基准。在模型中的任何一个元素均可以定义为一个上下文,例如类、属性、操做和关联。一旦咱们定义了上下文,就能够开始定义约束表达式饿。OCL是一种声明式语言,大部分表达式执行后会返回一个布尔值,也有一些表达式会用来选择一个单一值或者一个对象/值的集合。
能够看到,OCL语言和咱们在面向对象课程中所学到的JSF具备类似之处。不变量、前置条件、后置条件这些咱们已经耳熟能详的概念在OCL和JSF中都有体现,其所表明的含义也都大体相同。OCL中的监护条件则有点相似于JSF中的repOK方法(但并不彻底一致),即系统状态只要知足相关要求,就能够进行任意知足规格的调用。经过对这些约束的断言,咱们得以判断一个方法是否被正确调用或者是否被正确实现,至关于为一张空白的试卷制定了规则和答案。有了这些规则和答案,咱们再去具体撰写代码实现的时候,就有了相应的依据,能够自行判断出实现是否正确。此外,它们还能够帮助咱们撰写高质量的单元测试和正确性论证。
不一样之处在于,OCL语言是基于UML类图的,而JSF是基于代码自己的。从严谨性程度上来说,JSF也更高一筹,由于正如上文所说,布尔表达式(以集合论和谓词逻辑为基础)是最严谨的表达,其带来的约束的严谨性远胜于天然语言,且适合进行自动化验证。可是,形式化的数学语言并不适合全部人阅读,并且一些较为复杂的逻辑能够用简单的天然语言描述出来,但绝对没法用简单的数学语言去描述。所以,OCL和JSF各有其适用范围,二者的缺点正好是对方的优势。在工程开发中,两者互补为佳。
第一单元有三次做业:多项式加减、单傻瓜电梯、单捎带电梯。第一次做业的难度极小,主要是为了让同窗们初步接触Java编程,并将思惟从C语言的面向过程转为Java的面向对象。第二次做业的单傻瓜电梯稍有难度,相比于多项式加减,同窗们须要设计并实现的类变多了,大部分同窗在此次做业中第一次体验到了多个类协做带来的面向对象编程体验。第三次做业的难度陡然增长,指导书变得复杂了许多,算法也比较难以实现。尤为是同窗们在写单电梯的时候还须要考虑以后的多线程电梯,所以写的时候当心翼翼、举步维艰,生怕后面还须要重构(然而事实证实,多线程电梯仍是须要大规模重构…)。
第二单元能够说是整个课程体系中最难的一个单元。多线程的首次引入、指导书的繁杂、调试的不便、互测的博弈,这些元素使得这三次做业的难度陡然增长。第五次做业的多线程电梯中,同窗们须要经过多线程完成三部电梯的协做,并在捎带算法的基础上增长最小运动量原则。这些算法和以前的差异不大,难点在于多线程对调度器实现方法的影响。第六次做业是文件监视器,是指导书改动最频繁的一次,绝对难度并不大,复杂之处在于仔细理解指导书,并完成一个线程安全的设计。第七次做业的出租车调度是一系列做业的开始,此次做业中首次引入了设计原则,经过知足这些设计原则,同窗们能够在以后的增量设计中取得工做量上的减轻。
第三单元主打JSF。第9、10、十一次做业按部就班,逐步引入方法规格、类规格、带有继承的类规格,让同窗们能够经过完善规格,掌握作出一个良好设计的方法。这三次做业的难度不大、算法简单,可是要想写好JSF仍是须要下必定的功夫。
第四单元是测试与论证。一个程序若是没有正确性,那就不配称之为一个程序,第十三次做业的JUnit单元测试和第十四次做业的正确性论证是两种增长程序正确性的方法,经过这两次做业对第三次做业单捎带电梯的检查,同窗们基本掌握了测试和验证本身程序的方法,这种方法在以后的编程生涯中大有裨益。
这四个单元有一个清晰的主线:熟悉面向对象 à 多线程编程 à 设计与规格 à 测试与验证。这条主线是从一个对面向对象彻底没有概念的编程新手到能写出1000行具备优良设计风格的面向对象代码的人所必经的学习道路。四个单元之间按部就班,却又藕断丝连,对同窗们而言有着很大的训练价值。
在面向对象课程中,个人主要进步能够用一句话来归纳:从拿到需求(题目)就开始无脑写代码,变成了先思考再编码。我以为大二整个课程体系(包括但不限于计算机组成、面向对象)都是在训练咱们设计与实现分离的编程风格,事实上通过这一年的训练,个人设计能力和编码能力确实有了长足的提升。
此外,还有一些小的方面的进步。例如,之前我从未接触过多线程编程,也没有使用过Java的反射,面向对象课程的做业让我熟悉并掌握了这些颇有用的编程工具。
我并无参与过真正的工程化开发,所以接下来这一小节的内容都是基于个人想象。
我理解的工程化开发,重点在于协做。现代大型的软件工程规模已经大到了绝对不可能仅靠一我的单枪匹马就能完成开发,所以多人协做就显得尤其重要。当许多人一块儿完成一个大型项目的时候,因为每一个成员的能力不一样、对项目的理解也不一样,所以如何进行良好的沟通(包括语言层面的沟通和代码层面的沟通)成为了一个很大的问题。面向对象这门课程所教授的规格化设计就是解决沟通问题而进行的一个尝试。类的设计者经过规定前置规格来约定使用者的输入参数范围,经过撰写后置规格来提示使用者类和方法调用后的做用;类的使用者经过遵循前置规格来得到知足后置规格的调用结果,经过反复调用repOK方法得到类是否可以正常工做的反馈。
"协做"这个词的内涵是丰富的。为了沟通方便,完善的注释和优秀的代码风格是必需的,各类设计原则的知足是必须的,规范撰写的过程规格也是必需的。若是没有这些,一我的没法理解另外一我的的想法,没法对代码做出修改和重构,即便是代码撰写者本身,也会在一段时间以后,忘记以前代码的设计思路。所以,在工程化开发中,为了沟通和协做的须要,更多的时间应该花在规划、设计和测试上,真正的编码实现只占整个工程的一小部分。
工程化开发和本身作一个小项目彻底不同。在本身的项目中,任性没有关系,甚至有时候任性可以来带来更好的创意;然而在工程化开发中,任性只能给整个团队带来灾难。
但愿可以取消后面三次的出租车做业(即第三单元的三次以JSF为主要训练目的的做业),改成一个从零开始的、先撰写规格再完善代码的按部就班的项目。在现有的课程体系中,同窗们接触JSF的方式是经过先写代码再补充规格的方式,这使得大部分同窗难以体会到规格化设计对于一个工程项目的重要性。若是可以经过一个系列做业,让同窗们完总体会先设计再实现的过程,想必"设计无用论"的想法会少不少。
此外,在互测制度上,建议再完善一下JSFTools,为JSF制定一个统一的规范标准,以免互测双方对JSF标准的理解不一样而引起的争论。
最后,但愿面向对象课程能变得愈来愈好,而不是让愈来愈多的人去讨厌。