渡过OO的死劫,了解规格的意义——OO第三次博客总结
当熬过了一次次黑暗,迎接我们的却是被扣的惨不忍睹的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第三次博客总结的更多相关文章
- 迎来OO的曙光,总结规格的意义——OO第四次博客总结
一切都要结束了,砥砺前行~ 一.测试与正确性论证的效果差异 测试,顾名思义就是我们暴力用大量数据轰炸编写的程序的过程.日常的OO过程中,我们经常互相寻求“测试集”,正是因为测试使用特定数据对我们的功能 ...
- OO第三次博客作业——规格
OO第三次博客作业——规格 一.调研结果: 规格的历史: 引自博文链接:http://blog.sina.com.cn/s/blog_473d5bba010001x9.html 传统科学的特点是发现世 ...
- [BUAA OO]第三次博客作业
OO第三次博客作业 1. 规格化设计的发展 我认为,规格化设计主要源自于软件设计的两次危机.第一次是由于大量存在的goto语句,让当时被广泛应用的面向过程式的编程语言臃肿不堪,在逻辑性上与工程规模上鱼 ...
- 【作业3.0】HansBug的第三次博客规格总结
转眼间第三次作业了,似乎需要说点啥,那就说点. 规格&工业 说到这个,不得不提一下软件开发的发展史. 历史的进程 早在上世纪50年代,就已经有早期的编程语言出现,也开始有一些程序编写者出现(多 ...
- 2019年北航OO第三次博客总结
一.JML语言理论基础及其工具链 1. JML语言理论基础 JML是用于对Java程序进行规格化设计的一种表示语言,是一种行为接口规格语言(Behavior Interface Specificati ...
- 北航OO(2020)第三单元博客作业
一.JML理论基础及相关工具链 1.JML理论基础 该部分梳理本单元作业中涉及到的JML知识. 1.1注释结构 JML采用javadoc注释的方式来表示规格,且每行以@开头.通过使用//@annota ...
- OO第三次博客作业
一.规格化的发展历史 最早的程序设计都是面向机器,从一开始使用的机器语言,到后面的汇编语言,都是面向机器的语言,编写困难也容易出错.随着需求的发展,程序的编写从面向机器走向面向过程,但由于goto语句 ...
- oo第三次博客-JML规格
这三周的作业主要是围绕以JML来约束代码开发,以确保程序的正确性与鲁棒性. Part 1:三次作业的实现与bug 第一次作业没有任何算法和数据结构上的难度,对于Path和PathContainer的各 ...
- OO第三次博客作业---透过代码看设计
不得不说的JSF 经过前几次作业的煎熬.出租车的代码量不断地增多.而出租车问题在不断的完善,这也就牵涉到一个问题,那就是最初出租车程序的设计问题,如果一开始设计的就有问题的话,那么在后来的过程中就会遇 ...
随机推荐
- javascript---我对闭包的理解
一.闭包 闭包是一种特殊的对象.它由两部分构成:函数,以及创建该函数的环境.环境由闭包创建时在作用域中的任何局部变量组成. 如何理解这句话:以一个例子说明;(from MDN) funct ...
- python通过cx_oracle操作数据库过程简单记录
1.环境配置 环境配置过程中,需要关注软件版本是否一致,主要包括:oracle客户端版本.cx_oracle版本.python版本: 2.操作记录 (1)验证环境是否正常:(无报错即为正常) impo ...
- 电商 APP 下单页(俗称车2) 业务流程概要设计
购物车是电商APP的一个关键功能点,一般购物车包含 3-4 个页面,分别是: 1.购物车的商品列表页 2.商品下单页 3.订单付款页面 4.订单付款成功页面 由于现有购物车逻辑相对混乱,这里重新整理一 ...
- 深度学习框架caffe在ubuntu下的环境搭建
深度学习实验室服务器系统配置手册 目录: 一,显卡安装 二,U盘启动盘制作 三,系统安装 四,系统的基本配置 五,安装Nvidia驱动 六,安装cuda ...
- lua协程实现
协程是个很好的东西,它能做的事情与线程相似,区别在于:协程是使用者可控的,有API给使用者来暂停和继续执行,而线程由操作系统内核控制:另外,协程也更加轻量级.这样,在遇到某些可能阻塞的操作时,可以使用 ...
- msserver的update or insert语句
方案1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION; IF EXISTS (SELECT 1 FROM dbo.ta ...
- Hall定理
Hall定理 Tags:图论 zybl 二分图\(G=<V1,V2,E>\)中,\(|V1|<|V2|\),当且仅当\(V1\)中任意\(k(=1,2,3..|V1|)\)个顶点都与 ...
- windows7 64位机上配置支持GPU版(CUDA7.5)的OpenCV2.4.13操作步骤
很久之前在windows7 32位上配置过GPU版的opencv,可参考http://blog.csdn.net/fengbingchun/article/details/9831837 Window ...
- Loadrunner 性能指标
https://wenku.baidu.com/view/bf395a1db7360b4c2e3f64ca.html 希望能记住最好记住吧,这个很重要的. qq,979506750多交流
- MySQL5.7(二)数据库的基本操作
登录MySQL数据库 格式:mysql -u 用户名 -h 主机名或IP地址 -P 端口号 -p 密码