作业小结3
作业小结3
规格化设计的发展历史
最早的程序设计都是采用机器语言来编写的,直接使用二进制码来表示机器能够识别和执行的指令和数据。简单来说,就是直接编写0和1的序列来代表程序语言。例如:使用0000代表加载(LOAD),0001代表存储(STORE)等。
面向机器的语言通常情况下被认为是一种“低级语言”,为了解决面向机器的语言存在的问题,计算机科学的前辈们又创建了面向过程的语言。面向过程的语言被认为是一种“高级语言”,相比面向机器的语言来说,面向过程的语言已经不再关注机器本身的操作指令、存储等方面,而是关注如何一步一步的解决具体的问题,即:解决问题的过程,这应该也是面向过程说法的来由。
第一次软件危机:结构化程序设计。根本原因就是一些面向过程语言中的goto语句导致的面条式代码,极大的限制了程序的规模。结构化程序设计(英语:Structuredprogramming),一种编程范型。它采用子程序(函数就是一种子程序)、代码区块、for循环以及while循环等结构,来替换传统的goto。希望借此来改善计算机程序的明晰性、质量以及开发时间,并且避免写出面条式代码。
为了解决问题,在1968、1969年连续召开两次著名的NATO会议,会议正式创造了“软件危机”一词,并提出了针对性的解决方法“软件工程”。虽然“软件工程”提出之后也曾被视为软件领域的银弹,但后来事实证明,软件工程同样无法解决软件危机。
结构化程序设计的主要特点是抛弃goto语句,采取“自顶向下、逐步细化、模块化”的指导思想。结构化程序设计本质上还是一种面向过程的设计思想,但通过“自顶向下、逐步细化、模块化”的方法,将软件的复杂度控制在一定范围内,从而从整体上降低了软件开发的复杂度。结构化程序方法成为了1970年代软件开发的潮流。
规格化设计被重视的原因
所有的程序开发手册都包含了各种规则。一些习惯*程序人员可能对这些规则很不适应,但是在多个开发人员共同协作的情况下,这些规则是必须的。这不仅仅是为了开发效率来考虑,也是为了后期维护考虑。
代码规格化设计正是为了培养规范设计和编程,养成良好的习惯,增强软件产品的稳定,健壮,可靠性;同时也提高了软件的可读性,可以让程序员尽快而彻底地理解新的代码,使产品可维护性提高。
- 此外,还有如下好处:
- 减少维护成本。一个软件的生命周期中,80%的花费在于维护,另一方面,几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护,规范的编码减少人员变动带来的维护成本。
- 改善软件的可读性。可以让程序员尽快而彻底地理解新的代码。在一个团队中,代码也容易在程序员之间共享。
- 维护部门交付产品的规范形象。
规格bug的表格分析
规格bug类别 | 所对应方法的代码行数 | 产生原因 |
---|---|---|
第九次作业公测->JSF检查->不符合JSF规范 | 4 | 测试者说REQUIRES必须是一个布尔表达式不能是自然语言 |
第九次作业公测->JSF检查->Modifies不完整 | 5 | 改了this但没写 |
第九次作业公测->JSF检查->Requires不完整 | 8 | 测试者说有参数必须写requires |
第九次作业公测->JSF检查->Requires逻辑错误 | 4 | 测试者说有参数必须写requires |
第十次作业公测->规格检查->方法规格检查->JSF不符合规范 | 7 | 测试者说我没使用布尔表达式 |
第十一次作业公测->规格检查->抽象对象有效性实现检查:Overview是否明确抽象对象 | - | 测试者说Overview过于简陋,仅是名字的翻译,未体现出数据抽象思想 |
第十一次作业公测->规格检查->抽象对象有效性实现检查:Overview是否明确抽象对象 | - | 测试者说您的Taxi类、Settings类、Light类等等,Overview都相当的“抽象”啊,仅仅将名字翻译过来,或者是名词,或者是动词短语,并没有体现其真正含义所在。 |
第十一次作业公测->规格检查->方法规格检查->JSF不符合规范 | 1 | 测试者说规范的表示是:\result==this.point0; 而你是* @EFFECTS: return this.point0;。 |
前置条件与后置条件案例
前置条件
-
* @REQUIRES: other != null
改进:* @REQUIRES: other instanceof TrackableTaxi && other != null
说明:requires不够完整,对于某些方法应该更明确表明它的类型。 -
* @REQUIRES: this != null
改进:* @REQUIRES: None
说明:一般情况下this是不会等于null的,这大概是一句废话。 -
* @REQUIRES: None
改进:* @REQUIRES: this.property > 0
说明:该方法要求其它方法执行过后才能执行,而其它方法执行过后的当且仅当的标志是某个属性大于零。于是就这样子改了。 -
* @REQUIRES: this.property != null
改进:* @REQUIRES: None
说明:类中有一个repOK的方法,要求property不是Null。而property属性在构造方法中已经保证了不是Null。因此这是废话。 -
* @REQUIRES: x >=0 && x <= 79
改进:* @REQUIRES: x >= 0 && x <= MAP_MAX_LENGTH
说明:之前定义过常量MAP_MAX_LENGTH,这里就不适合写79了,不然以后要改地图尺寸时得改很多东西。其它所有常量同理。
后置条件
-
* @EFFECTS: return this.property
改进:* @EFFECTS: \result == this.property
说明:常见的语法问题。感谢测试者帮我找到这个问题。 -
* @EFFECTS: \result = this.property
改进:* @EFFECTS: \result == this.property
说明:常见的语法问题。感谢我舍友的测试者帮我舍友找到这个问题。 -
* @EFFECTS: this != null
改进:* @EFFECTS: this.property == value
说明:构造方法不能写的过于简单。 -
* @EFFECTS: 初始化该实例
改进:* @EFFECTS: this.property == value
说明:构造方法不能写的过于简单,即使使用自然语言。 -
* @EFFECTS: linkedList.contains(request); 改进:
* @EFFECTS: linkedList.contains(request) && linkedList.size = \old(linkedList).size + 1`
说明:没有列出所有变化。
功能bug与规格bug的聚集关系
方法名 | 功能bug数 | 规格bug数 |
---|---|---|
Line | 0 | 1 |
Taxi | 0 | 2 |
initTaxis | 0 | 1 |
checkSameUser | 0 | 1 |
getPoint0 | 0 | 1 |
看的出来,功能bug数跟规格bug数是相关的。如果一个方法连规格都写错了,那么它的功能是很难正确的。不论是构造方法还是普通的方法,都是这样子的。当然前提是写代码的人是根据规格来写的代码。
如果写规格的人和写代码的人不是同一个人,这个结论应该就很显然了。
我们常常做测试时,就对方法单独的测试,只要输入满足规格的requires,检查输出是否满足effects就好了。如果测试通过,且modifies正确,那么就较大概率没bug(因为并未进行完备的测试)。这样单独的方法对了,才是对的。
设计规格和撰写规格的思路和体会
先说体会吧!首先感谢课程组让我们训练设计和撰写规格。
老师课上总是强调设计规格的重要性,虽然大家写得很累,但是我觉得老师说得很对。设计规格确实是最重要的。尤其是在团队协作中。
所有的程序开发手册都包含了各种规则。我们可能对这些规则很不适应,但是在多个开发人员共同协作的情况下,这些规则是必须的。这不仅仅是为了开发效率来考虑,也是为了后期维护考虑。所以我觉得老师说的很对。
关于设计规格和撰写规格。我的思路如下:
首先,我们要根据设计需求,进行架构,做数据抽象,总结出几个类,弄清楚各个类的定义,弄清楚各个类之间如何交互,弄清楚各个类如何相互合作最终把这些设计需求来实现。
其次,根据数据抽象,弄清楚各个类需要哪些属性。
再然后,根据设计需求,弄清楚类需要哪些方法,而这些方法需要被哪个或哪些类调用。又或者它仅仅只是一个内部方法。弄清楚这个方法要实现什么样的功能。这里我想起来老师上课常常讲的话,就是写JSF时千万别去想算法。所以,这一步,先不考虑算法怎么实现,只考虑这个方法的功能。另外这一步要保证代码的逻辑是正确的。
再之后,开始写设计规格。根据上一布自己所想的,写好设计规格。注意require effect的正确性。
再之后,开始写代码,就实现那些方法。这里要注意算法的正确性,注意,如果有modify,可能还需要修改一下设计规格。
最后,测试程序,检查代码功能是否正确。如果功能错误,可能是在设计规格时逻辑错误,也可能是代码在具体实现时算法错误或者编码错误。要从这两个方向来找bug,最终得到正确的程序。
最后谢谢老师们谢谢助教们谢谢同学们!