渡过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 经过前几次作业的煎熬.出租车的代码量不断地增多.而出租车问题在不断的完善,这也就牵涉到一个问题,那就是最初出租车程序的设计问题,如果一开始设计的就有问题的话,那么在后来的过程中就会遇 ...
随机推荐
- Jmeter之post上传文件
上周五东西都收拾好了,然后被叫住加班,直接搞到凌晨一两点,原因是另一个项目的性能出了点问题.为此我抓包写了一下主业务流的接口,涉及到文件上传,记录一下吧. 一.创建线程组,添加各类组件 这个就不用说了 ...
- FFMpeg笔记(三) 音频处理基本概念及音频重采样
Android放音的采样率固定为44.1KHz,录音的采样率固定为8KHz,因此底层的音频设备驱动需要设置好这两个固定的采样率.如果上层传过来的采样率不符的话,需要进行resample重采样处理. 几 ...
- Win10/Ubuntu双系统安装常见问题
目录 1.win10重启无法进入BIOS 2.install ubuntu后黑屏 2.1 解决安装黑屏 2.2 安装英伟达显卡驱动 3.ubuntu中文系统注意 4.Windows系统时间异常 4.1 ...
- 新装Linux无法访问域名
昨天新安装Linux,发现ping百度ping不通: 经查询,得知是系统没有配置DNS域名服务器,百度搜索DNS域名服务器列表: 编辑 /etc/resolv.conf 文件,添加查询到的DNS服务器 ...
- windows下搭建permeate漏洞测试系统实战
最近一直在搭建漏洞测试环境练习. 在此期间遇到很多问题,但是通过学习都一一解决.通过写此文来记录遇到的问题和解决方法. 首先,在github上看到了一个不错的permeate渗透测试系统.于是想搭建拿 ...
- c语言计算功能
---恢复内容开始--- #include <stdio.h> int main(){int a1,a2,a3,a4; printf("请输入数值"); a4=0;wh ...
- mfc 类的析构函数
析构函数 自定义析构函数 一.析构函数 析构函数(destructor) 与构造函数相反,当对象生命周期结束时(例如对象所在的函数已调用完毕),系统自动执行析构函数.析构函数往往用来做“清理善 ...
- 函数内联inline
C++语言支持函数内联,其目的是为了提高函数的执行效率(速度). 宏的优点 在C程序中,可以用宏代码提高执行效率. 编译预处理器用拷贝宏代码的方式取代函数调用,省去了参数压栈,生成汇编语言的CALL调 ...
- 【CQOI2014】数三角形
题面 题解 考虑使用总数减去不合法的数量 首先将\(n, m\)都加上\(1\),将网格变成坐标系 总数即为\(\large\binom{n\times m}{3}\) 不合法的有三种情况: 三个点在 ...
- XAF-由于try catch导致的性能问题一例
前几天在制作PMMS系统时,有天突然发现性能问题下降严重,发布到客户机后,每点击一个按钮要花5-10秒的时间,与本机的200-600毫秒差距很大. 经过多处优化后没有效果. 后来想起,最近增加的功能是 ...