当熬过了一次次黑暗,迎接我们的却是被扣的惨不忍睹的JSF

┭┮﹏┭┮


一、总结调研

规格的历史

  传统科学的特点是发现世界,而软件的特点是构造世界。软件的最底层就是0,1,两个离散的值。程序设计语言的三次分离使软件技术产生了飞跃。

  程序设计的演变大致可以分成以下三个过程:

  1. 20世纪60年代以前,计算机刚刚投入实际使用,软件设计往往只是为了一个特定的应用而在指定的计算机上设计和编制,采用密切依赖于计算机的机器代码或汇编语言,软件的规模比较小,很少使用系统化的开发方式,此时的代码更多的是私人性质的,满足个人要求的程序

  2. 60年代中期,大容量、高速度的计算机出现,随之出现的是代码量急剧提升,复杂度急剧增长、程序可靠性问题突出的问题。结果化程序设计随之提出,它要求程序设计时以模块为单位,每个模块专职自己的工作,而要在模块间交流,在开发者和用户,开发者和开发者之间交流,就只需要相应接口即可;

  3. 80年代,面向对象的设计开始在业界大行其道,相比于单纯的结构化设计,面向对象的设计从审视问题的角度上就有了差异,程序发生了从围绕“行为”执行到围绕“客体”执行的变化,随之而来的,就是封装性地提高,可重用性的提高,可以说,面向对象进一步实现了结构化设计,是结构化设计更进一步的实现。说明是类型定义和操作描述,体是操作的具体实现。(具体的例子就是C++,Java等面向对象语言的类说明与类实现的分离。)解决方案设计只关注说明,实现时引用或者设计体。体的更改、置换不影响规格说明,保证了可移植性。支持多机系统,但要同样环境。此时产生了划时代的面向对象技术。在结构化越加明确的同时,开发者与设计者,开发者与用户,开发者与开发者之间的交流,就需要“抽象”来实现,因为彼此间不需要关心对方的实现方法,而只需要知道这个模块的接口有什么要求,会改什么,能做什么就行。通过规格化抽象,我们就能将这种交流变得高效,同时也降低了维护和修改的难度。

  4.基于构件开发:标准化的软件构件如同硬件IC,可插拔,使用者只用外特性,不计内部实现。Web Services:软件就是服务。分布式,跨平台,松耦合。

为什么规格很重要?

  这三次分离中的第二次就是OO课程要求我们去做的,也是面向对象的精髓。在大型项目的开发中,单个项目不可能由一个人完成。多人协作共同完成任务就意味着不仅仅需要知道自己的代码做了什么,还要知道别人的代码做了什么。自己的代码是自己写出来的,具体怎么实现,使用了什么样的数据结构,使用了什么样的算法,当然知道自己的程序会对什么产生影响,需要什么样的输入 。假设有个人写了一个方法,写好了,实现了,于是,他交了这份代码上去,你要使用这个方法,一看,这什么**玩意。花了时间适应了代码风格,知道要干嘛之后,写自己的代码进行测试,一测,bug,找了半天,发现是他和你的代码对于某个变量能不能修改有冲突。浪费了很长时间,再交给上面。。恶行循环。于是,规格的诞生可以说是拯救了这个局面。拿到代码,看前置条件,副作用,后置条件,就很清晰,永无bug(当然是不可能的)。

  软件工程行业代码也越来越复杂,多人协作是必不可少,规格在我看来是代码风格的调和剂,多人项目运作的润滑油。


二、规格bug及分析

第九次作业

Effects不完整 92行:@EFFECTS: true ==> (parse the input); 
Effects内容为实现算法 34行:@EFFECTS: true ==> (read the map);

>这次作业两个bug的产生一是因为方法写的太过冗杂,导致Effects描述功能并不能描述的很是详尽,二是一些比较奇怪的方法由于个人能力有限只能用自然语言去尽量的描述,比如上面的对于读取地图的方法。

第十次作业

Effects不完整 77行:@THREAD_EFFECTS: this.MS;
Modified不完整 59行:@MODIFIES: System.out,this.output,this.rl;缺少

>这次的bug和上次基本一样吧。也就是方法没有设计得很好。从第九次作业开始,很多的方法都是沿用的之前的作业,而之前已经写好的方法重新加上规格会导致有一些本末倒置的意味,并且课下发放的JSF并不能完全解决我们对于规格的理解,所以在编写程序规格时难免出现一些问题,归结原因还是不熟练以及缺乏类似的思想。至于新增的方法没有写JSF还是因为自己先写了方法本身,而非老师说的先写规格再写代码。总之规格写的很迷就是了。

第十一次作业

无JSF bug


三、不好的规格写法及改进

前置1:赋值“=”应该改为“==”

    /**
*@REQUIRES: s!=null,s=TAKING||s=WAITING||s=SERVING||s=STOP;
*@MODIFIES: this.status;
*@EFFECTS:
* status==s;
*/
public void SetStatus(TaxiStatus s){
this.status=s;
}
//正确写法
/**
*@REQUIRES: s!=null,s==TAKING||s==WAITING||s==SERVING||s==STOP;
*@MODIFIES: this.status;
*@EFFECTS:
* status==s;
*/
public void SetStatus(TaxiStatus s){
this.status=s;
}

前置2:前置条件必须为布尔表达式

/**
* @REQUIRES: index is in 0-100;
* @MODIFIES:this.index;
* @EFFECTS: this.index == index;
*/
public TaxiCar(int index) {
this.index = index;
}
//正确写法
/**
* @REQUIRES: 0<=index<100;
* @MODIFIES:this.index;
* @EFFECTS: this.index == index;
*/
public TaxiCar(int index) {
this.index = index;
}

前置3:直接写null不判断对象存在与否

  /**
* @REQUIRES: None;
* @MODIFIES: this.req; this.position;
* @EFFECTS: this.req == req; this.position == position;
*/
public Record(Request req, Point p) {
this.req = req;
this.position = p;
}
//正确写法
/**
* @REQUIRES: req != null && p != null;
* @MODIFIES: this.req; this.position;
* @EFFECTS: this.req == req; this.position == position;
*/
public Record(Request req, Point p) {
this.req = req;
this.position = p;
}

前置4:对于数字类型范围判断遗漏或错误

/**
*@REQUIRES:id!=null;
*@EFFECTS:
* \result==squad[id];
*/
public Taxi get(int id){
return this.squad[id];
}
//正确写法
/**
*@REQUIRES:id!=null,0<=id<=99;
*@EFFECTS:
* \result==squad[id];
*/
public Taxi get(int id){
return this.squad[id];
}

前置5:由于粗心缺少括号引起的逻辑错误

/**
* @REQUIRES: req != null && index >= 80 || index < 0;
* @MODIFIES: this.req; this.index;
* @EFFECTS: this.req == req; this.index;
*/
public Record(Request req, int index) {
this.req = req;
this.index = index;
}
//正确写法
/**
* @REQUIRES: req != null && (index >= 80 || index < 0);
* @MODIFIES: this.req; this.index;
* @EFFECTS: this.req == req; this.index;
*/
public Record(Request req, int index) {
this.req = req;
this.index = index;
}

后置1:尽量不要使用自然语言(与前置不同,不是必须的)

/**
* @REQUIRES: req != null;
* @MODIFIES: None;
* @EFFECTS: 判断起始点与目标点是否为同一个点
*/
public boolean samedest(Request req) {
if(req.getSrc().equals(req.getDst())) {
return true;
}
else
return false;
}
//正确写法
/**
* @REQUIRES: req != null;
* @MODIFIES: None;
* @EFFECTS: (req.getSrc() == req.getDst()) ==> \result == true;
* (req.getSrc() != req.getDst()) ==> \result == false;
*/
public boolean samedest(Request req) {
if(req.getSrc().equals(req.getDst())) {
return true;
}
else
return false;
}

后置2:缺少程的规格

        /**
*@MODIFIES: System.out;
*@EFFECTS:
* true==>(do the things below in an endless loop)==>(wait())==>(dispatch());
*/
public void run(){
while(true){
try {
synchronized(this.MS){
this.MS.wait();
this.dispatch();
}
//System.out.println(System.currentTimeMillis());
} catch (Throwable e) {
System.out.println("Error in Scheduler");
System.exit();
}
}
}
//正确写法
/**
*@MODIFIES: System.out;
*@EFFECTS:
* true==>(do the things below in an endless loop)==>(wait())==>(dispatch());
*@THREAD_EFFECTS: this.MS;
*/
public void run(){
while(true){
try {
synchronized(this.MS){
this.MS.wait();
this.dispatch();
}
//System.out.println(System.currentTimeMillis());
} catch (Throwable e) {
System.out.println("Error in Scheduler");
System.exit();
}
}
}

后置3:未处理异常(没有说明)

public static int min (int[ ] a) throws NullPointerException, EmptyException
/**@ EFFECTS: \result == \min a;
*/
//正确写法
public static int min (int[ ] a) throws NullPointerException, EmptyException
/**@ EFFECTS: normal_behavior
\result == \min a;
(a == null) ==> exceptional_behavior (NullPointerException);
(a.length == 0) ==> exceptional_behavior (EmptyException);
*/

后置4:方法行数太多导致描述遗漏或无法描述

    /**
*@MODIFIES: this.light,this.lightmap;
*@EFFECTS:
* Change the lights
*/
public void Change(){
for(int i=;i<;i++){
for(int j=;j<;j++){
if(Main.light[i][j]==){
Main.light[i][j]=;
guigv.lightmap[i][j]=;
Main.TG.SetLightStatus(new Point(i,j),);
}
else if(Main.light[i][j]==){
Main.light[i][j]=;
guigv.lightmap[i][j]=;
Main.TG.SetLightStatus(new Point(i,j),);
}
}
}
}
//正确写法
/**
*@MODIFIES: this.light,this.lightmap;
*@EFFECTS:
* (\all int i,j,k;0<=i,j<80,light[i][j]>0)==>
* ((light[i][j]==1)==>(light[i][j]==2&&lightmap[i][j]==1)&&(light[i][j]==2)==>(light[i][j]==1&&lightmap[i][j]==2));
*/
public void Change(){
for(int i=;i<;i++){
for(int j=;j<;j++){
if(Main.light[i][j]==){
Main.light[i][j]=;
guigv.lightmap[i][j]=;
Main.TG.SetLightStatus(new Point(i,j),);
}
else if(Main.light[i][j]==){
Main.light[i][j]=;
guigv.lightmap[i][j]=;
Main.TG.SetLightStatus(new Point(i,j),);
}
}
}
}

后置5:描述逻辑不正确或不准确

/**
* @REQUIRES : None;
* @MODIFIES : this.AskList;
* @EFFECTS : AskList.contains(ask);
*/
public synchronized void addAsk(Ask ask){
AskList.add(ask);
}
//正确写法
/**
* @REQUIRES : None;
* @MODIFIES : this.AskList;
* @EFFECTS : AskList.contains(ask) && AskList.size == \old(AskList).size + 1;
*/
public synchronized void addAsk(Ask ask){
AskList.add(ask);
}

四、功能bug与规格bug的聚焦关系

作业次数 功能bug数 规格bug数 聚焦关系
第九次 2 2 无明显关系
第十次 7 2 无明显关系
第十一次 2 0 无明显关系

其实从bug数上根本看不出来在功能上和规格上的bug之间有什么明显的关系。但我确实比较倾向于一个好的规格,必然是一个代码行数比较少,而且功能单一且明确的方法所具备的。而这样的代码写出来在功能上的bug也十分容易被发现并改正。从整个程序的设计来考虑,当一个方法的规格复杂时,必然需要更多的篇幅和代码来进行实现,出现bug的几率也会越大。所以我们以后写代码一定要以规格来写代码。


五、心得体会

  其实说关于设计规格和撰写规格的思路与体会也并没有什么特别的,因为我们在训练撰写规格是在以前写的方法的基础上去完善的。但是由于以前写的方法功能比较杂糅,而且个别方法特别长,所以并不是很好地去撰写一个比较标准的规格。我们也没有进行一个先写规格再根据规格写代码的训练,对于这方面我也没有经验。但是我还是觉的这方面的训练是十分有必要的。因为一个好的代码一定是可移植好维护的,而设计一个好的规格是写一个好的代码的基础。

  最后希望大家都能善良一点,开心一点,共同迎接OO胜利的曙光咯~

渡过OO的死劫,了解规格的意义——OO第三次博客总结的更多相关文章

  1. 迎来OO的曙光,总结规格的意义——OO第四次博客总结

    一切都要结束了,砥砺前行~ 一.测试与正确性论证的效果差异 测试,顾名思义就是我们暴力用大量数据轰炸编写的程序的过程.日常的OO过程中,我们经常互相寻求“测试集”,正是因为测试使用特定数据对我们的功能 ...

  2. OO第三次博客作业——规格

    OO第三次博客作业——规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...

  3. [BUAA OO]第三次博客作业

    OO第三次博客作业 1. 规格化设计的发展 我认为,规格化设计主要源自于软件设计的两次危机.第一次是由于大量存在的goto语句,让当时被广泛应用的面向过程式的编程语言臃肿不堪,在逻辑性上与工程规模上鱼 ...

  4. 【作业3.0】HansBug的第三次博客规格总结

    转眼间第三次作业了,似乎需要说点啥,那就说点. 规格&工业 说到这个,不得不提一下软件开发的发展史. 历史的进程 早在上世纪50年代,就已经有早期的编程语言出现,也开始有一些程序编写者出现(多 ...

  5. 2019年北航OO第三次博客总结

    一.JML语言理论基础及其工具链 1. JML语言理论基础 JML是用于对Java程序进行规格化设计的一种表示语言,是一种行为接口规格语言(Behavior Interface Specificati ...

  6. 北航OO(2020)第三单元博客作业

    一.JML理论基础及相关工具链 1.JML理论基础 该部分梳理本单元作业中涉及到的JML知识. 1.1注释结构 JML采用javadoc注释的方式来表示规格,且每行以@开头.通过使用//@annota ...

  7. OO第三次博客作业

    一.规格化的发展历史 最早的程序设计都是面向机器,从一开始使用的机器语言,到后面的汇编语言,都是面向机器的语言,编写困难也容易出错.随着需求的发展,程序的编写从面向机器走向面向过程,但由于goto语句 ...

  8. oo第三次博客-JML规格

    这三周的作业主要是围绕以JML来约束代码开发,以确保程序的正确性与鲁棒性. Part 1:三次作业的实现与bug 第一次作业没有任何算法和数据结构上的难度,对于Path和PathContainer的各 ...

  9. OO第三次博客作业---透过代码看设计

    不得不说的JSF 经过前几次作业的煎熬.出租车的代码量不断地增多.而出租车问题在不断的完善,这也就牵涉到一个问题,那就是最初出租车程序的设计问题,如果一开始设计的就有问题的话,那么在后来的过程中就会遇 ...

随机推荐

  1. 1349: Taking Pebbles (博弈 打表找规律)

    1349: Taking Pebbles Submit Page    Summary    Time Limit: 1 Sec     Memory Limit: 128 Mb     Submit ...

  2. mysql8.0.15安装

    1. 官网下载mysql,此处下载的是.zip文件 2. 解压下载的文件夹,并且配置环境变量:Path : E:\mysql-8.0.15-winx64\bin 3. 配置my.ini文件 4. 以管 ...

  3. CentOS 7 在最小化安装之后进行安装Apache服务,httpd-2.4.34

    此博文是CentOS 7 在最小化安装之后进行安装Apache服务的操作步骤,httpd版本为httpd-2.4.34. 一.基本服务环境搭建 安装apache需要的基本环境:apr apr-util ...

  4. Redis高级应用——2

    Redis-事务 Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证: 事务是一个单独的隔离操作,事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中,不会被其他客户端发送来的 ...

  5. webservice的 发布

    webservice的 发布一般都是使用WSDL(web service descriptive language)文件的样式来发布的,在WSDL文件里面,包含这个webservice暴露在外面可供使 ...

  6. flex 自定义组件的编写

    使用flex也很久了,也改过别人写的flex自定义组件,但是就是没有系统的研究下flex组件的编写步骤,和要注意的东西,在这里我参照一本书中的例子,好好的理解下,也为了巩固下自己对flex的理解! 1 ...

  7. TCP/IP协议、HTTP协议

    一.序: TCP/IP协议是程序开发的基础知识,我们都知道它可以实现不同计算机之间的通信,它是什么意思?怎么实现通信的? 二.TCP/IP协议: (1)协议:约定 (2)tcp/ip:tcp是传输控制 ...

  8. Install-Package:QRCoder已拥有为System.Drawing.Common定义的依赖项

    error_log PM> Install-Package QRCoder -Version 1.3.3 Install-Package : "QRCoder"已拥有为&qu ...

  9. Flutter - 左右侧滑菜单:drawer和endDrawer

    侧滑菜单可以从左面滑出,也可以从右面滑出.在Scaffold中有drawer和endDrawer两个参数,分别对应左边的菜单和右边的菜单. drawer: new Drawer( child: new ...

  10. Python学习之路:MINST实战第一版

    1.项目介绍: 搭建浅层神经网络完成MNIST数字图像的识别. 2.详细步骤: (1)将二维图像转成一维,MNIST图像大小为28*28,转成一维就是784. (2)定义好神经网络的相关参数: # M ...