一、规格化设计
让我们从程序设计的演变分析来看下规格设计
1950年代,第一次分离,主程序和子程序的分离
程序结构模型是树状模型,子程序可先于主程序编写。通过使用库函数来简化编程,实现最初的代码重用。产生基本的软件开发过程:分析—设计—编码—测试,使大型软件系统的开发成为可能
1975—1980年代,第二次分离,规格说明和体的分离
说明是类型定义和操作描述,体是操作的具体实现。(具体的例子就是C++,Java等面向对象语言的类说明与类实现的分离。)解决方案设计只关注说明,实现时引用或者设计体。体的更改、置换不影响规格说明,保证了可移植性。支持多机系统,但要同样环境。此时产生了划时代的面向对象技术。
1995—2000年代,第三次分离,对象使用和对象实现的分离
基于构件开发:标准化的软件构件如同硬件IC,可插拔,使用者只用外特性,不计内部实现。
Web Services:软件就是服务。分布式,跨平台,松耦合。
从个人的角度来看,规格化的设计是很有必要的,按照标准的JSF去写代码,可以保证代码的质量,提高代码的复用性。在团队工作中,代码不只是给自己看的,为了让自己代码的使用方法很快被别人了解,代码规格就十分重要了。
二、规格BUG分析
第九次作业
无
第十次作业
编号 | 规格BUG类别 | 所在方法 | 方法代码行数 |
1 | 针对构造方法,初始状态repOK为真 | Request.repOK() | 12 |
2 | Modifies不完整 | Taxi() | 10 |
3 | Requires逻辑错误 | RequestQueue.size() | 1 |
4 | Requires逻辑错误 | TaxiGroup.statusInquire() | 1 |
1.我写了两个不同参数的Requeset的构造器,其中一个构造器并未对PrintStream属性进行初始化,导致repOK检查时返回false
2.Taxi构造器方法对出租车的所有属性进行了初始化设置,但我在Modifies中少写了几个属性
3.(这个属于手残的错误)粘贴上个方法的Requires下来的时候忘了修改了
4.此方法传入的参数的为出租车的编号,理应在0~99的范围内,否则可能发生数组越界错误,我忽视了这一点,没有在Requires中声明
第十一次作业
编号 | 规格BUG类别 | 所在方法 | 方法代码行数 |
1 | JSF不符合规范 | TaxiGroup.run() | 29 |
2 | JSF不符合规范 | Scheduler.run() | 100 |
3 | JSF不符合规范 | InputHandler.run() | 75 |
4 | Modifies不完整 | Request.set_add() | 73 |
5 | Modifies不完整 | LightMap.run() | 20 |
6 | Requires不完整 | lightBlockJudge() | 15 |
7 | Requires不完整 | RequestQueue.remove() | 1 |
8 | Modifies不完整 | Map.mapInit() | 54 |
9 | Modifies逻辑错误 | Map.pointbfs() | 42 |
1.我在run方法中进行了线程控制,synchronized了两个对象,而没有书写多线程控制规格
2.依然是上个原因,进行synchronized控制而没有书写多线程控制规格
3.依然是上个原因,进行synchronized控制而没有书写多线程控制规格
4.在方法中对HashSet进行了修改,但没有在Modifies中声明
5.在方法中对lightMap进行了修改,但没有在Modifies中声明
6.本次作业中新添加了此方法,忘了写规格(很是尴尬)
7.方法为从队列中移除下标为传入参数的元素,但在Requires中只写了index<queue.size,没有对负数进行限制,应为0<=index<queue.size,避免产生越界错误
8.在方法中对distance进行了修改,但没有在Modifies中声明
9.在方法中只是对局部变量view进行了修改,不应写在Modifies声明中
规格BUG产生的原因
首先,当然是自己的问题,对于大量的方法补充JSF,各种粗心大意导致被扣分,除了下标为负没有考虑到,其他的均是很低级的错误;其次,第一次写规格是在第九次作业,而这次作业是承袭第七次作业,因此大家都是后来对很多方法补充规格,这样就违反了本应有的顺序;最后,对于规格化的书写教学部分感觉不是很详细,后来下发的JSF样例包含的情况有限,导致大家对规格的理解不是那么深刻,写出很多不标准的规格。
三、前置条件和后置条件改进
前置条件
1.未限制数据范围
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: None; 4 * @EFFECTS: esult == (taxi_group[id].status_get()); 5 */ 6 public Status statusInquire(int id) { 7 return taxi_group[id].status_get(); 8 }
改进方法
1 /** 2 * @REQUIRES: 0<=id<=99; 3 * @MODIFIES: None; 4 * @EFFECTS: esult == (taxi_group[id].status_get()); 5 */ 6 public Status statusInquire(int id) { 7 return taxi_group[id].status_get(); 8 }
2.构造方法未进行限制
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: this.rq; 4 * @EFFECTS: this.rq == rq; 5 */ 6 public InputHandler(RequestQueue rq) { 7 this.rq = rq; 8 }
改进方法
1 /** 2 * @REQUIRES: rq != null; 3 * @MODIFIES: this.rq; 4 * @EFFECTS: this.rq == rq; 5 */ 6 public InputHandler(RequestQueue rq) { 7 this.rq = rq; 8 }
3.未进行类型判断
1 /** 2 * @REQUIRES: r != null; 3 * @MODIFIES: None; 4 * @EFFECTS: (r.src in map && r.dst in map) ==> esult true; 5 * !(r.src in map && r.dst in map) ==> esult false; 6 */ 7 public boolean formatCheck(Request r) { 8 if(r.srcxGet()>=80 || r.srcyGet()>=80) return false; 9 if(r.dstxGet()>=80 || r.dstyGet()>=80) return false; 10 if(r.dstGet()==r.srcGet()) return false; 11 return true; 12 }
改进方法
1 /** 2 * @REQUIRES: r != null && r instanceof Request; 3 * @MODIFIES: None; 4 * @EFFECTS: (r.src in map && r.dst in map) ==> esult true; 5 * !(r.src in map && r.dst in map) ==> esult false; 6 */ 7 public boolean formatCheck(Request r) { 8 if(r.srcxGet()>=80 || r.srcyGet()>=80) return false; 9 if(r.dstxGet()>=80 || r.dstyGet()>=80) return false; 10 if(r.dstGet()==r.srcGet()) return false; 11 return true; 12 }
4.类型描述不完整
1 /** 2 * @REQUIRES: rq != null && rq instance of RequestQueue && taxi_group != null && taxi_group instanceof Taxi[]; 3 * @MODIFIES: rq,this.tg; 4 * @EFFECTS: this.rq == rq; 5 * this.tg == taxi_group; 6 */ 7 public Scheduler(RequestQueue rq, Taxi[] taxi_group) { 8 this.rq = rq; 9 this.tg = taxi_group; 10 }
改进方法
1 /** 2 * @REQUIRES: rq != null && rq instance of RequestQueue && taxi_group != null && all int i; 0<=i<=taxi_group.length; taxi_group[i] instanceof Taxi; 3 * @MODIFIES: rq,this.tg; 4 * @EFFECTS: this.rq == rq; 5 * this.tg == taxi_group; 6 */ 7 public Scheduler(RequestQueue rq, Taxi[] taxi_group) { 8 this.rq = rq; 9 this.tg = taxi_group; 10 }
5.未进行范围限制
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: None; 4 * @EFFECTS: esult == (flowmap.get(Key(x1,y1,x2,y2))==null ? 0 :flowmap.get(Key(x1,y1,x2,y2))); 5 * @THREAD_REQUIRES: locked(this); 6 * @THREAD_EFFECTS: locked(); 7 */ 8 public static int GetFlow(int x1,int y1,int x2,int y2){ 9 synchronized (flowmap) { 10 return flowmap.get(Key(x1,y1,x2,y2))==null ? 0 :flowmap.get(Key(x1,y1,x2,y2)); 11 } 12 }
改进方法
1 /** 2 * @REQUIRES: 0<=x1<=79 && 0<=y1<=79 && 0<=x2<=79 && 0<=y2<=79; 3 * @MODIFIES: None; 4 * @EFFECTS: esult == (flowmap.get(Key(x1,y1,x2,y2))==null ? 0 :flowmap.get(Key(x1,y1,x2,y2))); 5 * @THREAD_REQUIRES: locked(this); 6 * @THREAD_EFFECTS: locked(); 7 */ 8 public static int GetFlow(int x1,int y1,int x2,int y2){//查询流量信息 9 synchronized (flowmap) { 10 return flowmap.get(Key(x1,y1,x2,y2))==null ? 0 :flowmap.get(Key(x1,y1,x2,y2)); 11 } 12 }
后置条件
1.尽量避免使用自然语言
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: gap, lightTime; 4 * @EFFECTS: true ==> (Initialize the LightMap's information); 5 */ 6 public LightMap() { 7 gap = 500 + new Random().nextInt(501); 8 lightTime = gap; 9 }
改进方法
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: gap, lightTime; 4 * @EFFECTS: gap = 500 + new Random().nextInt(501); 5 lightTime = gap; 6 */ 7 public LightMap() { 8 gap = 500 + new Random().nextInt(501); 9 lightTime = gap; 10 }
2.缺少多线程规格
1 /** 2 * @EFFECTS: esult == queue.size; 3 */ 4 public synchronized int size() { 5 return this.queue.size(); 6 }
改进方法
1 /** 2 * @EFFECTS: esult == queue.size; 3 * @THREAD_REQUIRES: locked(this); 4 * @THREAD_EFFECTS: locked(); 5 */ 6 public synchronized int size() { 7 return this.queue.size(); 8 }
3.不符合布尔表达式规范
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: rq,this.tg; 4 * @EFFECTS: this.rq = rq; 5 * this.tg = taxi_group; 6 */ 7 public Scheduler(RequestQueue rq, Taxi[] taxi_group) { 8 this.rq = rq; 9 this.tg = taxi_group; 10 }
改进方法
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: rq,this.tg; 4 * @EFFECTS: this.rq == rq; 5 * this.tg == taxi_group; 6 */ 7 public Scheduler(RequestQueue rq, Taxi[] taxi_group) { 8 this.rq = rq; 9 this.tg = taxi_group; 10 }
4.尽量避免使用自然语言
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: this.id,this.pos,this.posx,this.posy,this.pre,this.status,this.waittime,this.taxiTime,this.blockFlag; 4 * @EFFECTS: true ==> (Initialize taxi); 5 */ 6 public Taxi(int id) { 7 this.id = id; 8 Random rand = new Random(); 9 this.pos = rand.nextInt(6400); 10 pre = pos; 11 xy_set(); 12 this.status = Status.WAITING; 13 this.waittime = 0; 14 taxiTime = 500; 15 blockFlag = false; 16 Main.taxigui.SetTaxiStatus(id, new Point(posx,posy), 2); 17 }
改进方法
1 /** 2 * @REQUIRES: None; 3 * @MODIFIES: this.id,this.pos,this.posx,this.posy,this.pre,this.status,this.waittime,this.taxiTime,this.blockFlag; 4 * @EFFECTS: this.id == id; 5 rand == new Random(); 6 this.pos == rand.nextInt(6400); 7 pre == pos; 8 this.status == Status.WAITING; 9 this.waittime == 0; 10 taxiTime == 500; 11 blockFlag == false; 12 */ 13 public Taxi(int id) { 14 this.id = id; 15 Random rand = new Random(); 16 this.pos = rand.nextInt(6400); 17 pre = pos; 18 xy_set(); 19 this.status = Status.WAITING; 20 this.waittime = 0; 21 taxiTime = 500; 22 blockFlag = false; 23 Main.taxigui.SetTaxiStatus(id, new Point(posx,posy), 2); 24 }
5.忽略函数中调用了其他函数
1 /** 2 * @REQUIRES: map.contains(pos); 3 * @MODIFIES: this.posx,this.posy; 4 * @EFFECTS: this.pos == pos; 5 */ 6 public void pos_set(int pos) { 7 this.pos = pos; 8 xy_set(); 9 }
改进方法
1 /** 2 * @REQUIRES: map.contains(pos); 3 * @MODIFIES: this.posx,this.posy; 4 * @EFFECTS: this.pos == pos; 5 * posy == pos%80; 6 * posx == (pos-posy)/80; 7 */ 8 public void pos_set(int pos) { 9 this.pos = pos; 10 xy_set(); 11 }
四、聚集关系分析
方法名 | 错误原因 | 功能BUG数 | 规格BUG数 | |
作业9 | Taxi.work() | 忘了使用addFlow()增加流量 | 1 | 0 |
作业10 | Taxi.work() | 在STOP状态对流量处理不当 | 1 | 0 |
TaxiGroup.run() | 上一个问题的连带问题 | 1 | 0 | |
作业11 | LightMap.lightMapInit() | 没有过滤空格和制表符 | 1 | 0 |
通过结果来看似乎功能bug和规格bug并没有什么关系,因为其实是先完成了作业的功能要求才添加的规格,而规格的bug是由于在写规格过程中对函数没有细致分析造成的。所以本质上来说还是由于对规格的理解不当造成的.
五、设计规格和撰写规格的基本思路和体会
很惭愧,我的代码规格都是写好函数之后才补上去的,所以谈不上设计,只能写一点撰写的方法了。
对于Requires部分,要对函数传入的参数,函数中用到的一些全局变量进行严格约束,从类型到范围都要考虑全面。Modifies部分要对函数中修改的变量列出来,不重不漏。Effects部分就比较麻烦了,要使用布尔表达式对result的情况进行撰写,需要对函数的作用进行良好的抽象化表达,尽量避免使用自然语言,才能写出一份良好的规格。
六、碎碎念
这三次作业其实写的并不是很累,基本都是到周二才开始动手写,从结果来看,功能上的bug有自己实现方法有问题的,也有手残的,然而JSF被扣的分基本上都是因为手残,得承认自己有不足,但是这门课面向运气的测试依然让人很不爽。得承认JSF对于工程化有重要作用,不过在这么课中,JSF用来被扣分还是有待商榷的~