OO第三次博客作业
一、规格化设计的发展历史
首先来说一说规格化设计的发展历史,由于所能找到的资源实在有限,所以只能写这些了。
1950年代—1960年代初,手工艺式的程序设计方法,高德纳把程序称为艺术品。
1960年代末—1970年代初,出现软件危机:一方面需要大量的软件系统,如操作系统、数据库管理系统; 另一方面,软件研制周期长,可靠性差,维护困难。编程的重点:希望编写出的程序结构清晰、易阅读、易修改、易验证,即得到好结构的程序。
1968年,北大西洋公约组织(NATO)在西德召开了第一次软件工程会议,分析了危机的局面,研究了问题的根源,第一次提出了用工程学的办法解决软件研制和生产的问题,本次会议可以算做是软件发展史上的一个重要的里程碑。同时,Dijkstra 提出了“GOTO是有害的”,希望通过程序的静态结构的良好性保证程序的动态运行的正确性。
1969年,国际信息处理协会(IFIP)成立了“程序设计方法学工作组”,专门研究程序设计方法学,程序设计从手工艺式向工程化的方法迈进。同年,Wirth 提出采用“ 自顶向下逐步求精、分而治之” 的原则进行大型程序的设计。其基本思想是:从欲求解的原问题出发,运用科学抽象的方法,把它分解成若干相对独立的小问题,依次细化,直至各个小问题获得解决为止。
这便是程序设计初期的发展,而从程序设计发展开始至今,程序设计的方法一直在不断的进步中。我们期待会有越来越多的人认识与学习这种方法,并将其不断发展壮大。
二、规格bug分析
1、第九次作业
本次作业遇到了一个比较认真的测试者,再加之第八次作业的测试者没有对我的程序进行测试。导致这次作业被发现了不少的bug。在这里陈述,并进行反思。
JSF存在bug
REQUIRES不完整:2
MODIFIES不完整:1
虽然测试者只给调出这几个bug,但是实际上我写的JSF还是存在许多漏洞的,在这里挑典型分析一下。
MODIFIES不完整:
public boolean checksame(Req r) /** * @EFFECTS:(\all Req r;r match;\result == true); * (\all Req r;r not match;\result == false); */ { if (ReqTray.isEmpty()) { ReqTray.add(r); return true; } else if (ReqTray.get(ReqTray.size() - 1).getTime() == r.getTime()) { for (Req now : ReqTray) { if (now.equals(r)) { System.out.println("指令"+r.getReqnum()+"同质"); return false; } } ReqTray.add(r); return true; } else { ReqTray.clear(); ReqTray.add(r); return true; }
可以发现这段代码的MODIFIES写的并不完整,且EFFECTS也存在相应的问题。修改如下:
public boolean checksame(Req r) /** * @REQUIRES: r != null; * @MODIFIES: ReqTray; * @EFFECTS: (\result == false)==>(\old(\this).contains(r) == true); * (\result == true)==>(\this.size == \old(\this).size+1) && (\this.contains(a) == true)&&(\result == true); */ { if (ReqTray.isEmpty()) { ReqTray.add(r); return true; } else if (ReqTray.get(ReqTray.size() - 1).getTime() == r.getTime()) { for (Req now : ReqTray) { if (now.equals(r)) { System.out.println("指令"+r.getReqnum()+"同质"); return false; } } ReqTray.add(r); return true; } else { ReqTray.clear(); ReqTray.add(r); return true; } }
REQUIRES不完整:
public synchronized void addTaxi(int amount) /** * @MODIFIES:ArrayList<Taxi> Taxi; * @EFFECTS:(\all Taxi e;0 <= x && x <= 80 && 0 <= y && y <= 80 && src.x - 2 <=x && x <= src.x + 2 && src.y - 2 <= y && y <= src.y + 2 && nowTaxi.getState() == 2;Taxi.add(e)) */ { long Start = gv.getTime(); int i = 0;// 检查次数变量 Point src = this.src; ShortWay.pointbfs(src);// 在抢单时计算路径长度 while (gv.getTime() - Start <= 7500) { for (i = 0; i < amount; i++) { Taxi nowTaxi = Alltaxi.All.get(i); // System.out.println("检查出租车" + nowTaxi.getNum() + "能否接单"); int x = nowTaxi.getLoc_i(); int y = nowTaxi.getLoc_j(); // System.out.println("出租车" + nowTaxi.getNum() + "检查接单时位置" + "(" + x + "," + y + // ")"); if (0 <= x && x <= 80 && 0 <= y && y <= 80 && src.x - 2 <= x && x <= src.x + 2 && src.y - 2 <= y && y <= src.y + 2 && nowTaxi.getState() == 2) { // System.out.println(src.i); // System.out.println(src.j); if (Taxi_flag[i] == false) { Catch.add(nowTaxi); nowTaxi.addCredit(1); Taxi_flag[i] = true; System.out.println("将出租车" + nowTaxi.getNum() + "加入抢单队列"); } } } gv.stay(250); } }
同样的这段代码的JSF缺少REQUIRES,同时MODIFIES和EFFECTS也存在不同程度上的问题。修改如下:
public synchronized void addTaxi(int amount) /** * @REQUIRES: amount >= 0; * @MODIFIES: Taxi; * @EFFECTS: (\all Taxi e;Catch.contain(e) == true) ==> (0<=a.x<=79 && src.x-2<=a.x<=src.x+2) * ||(0<=a.y<=79 && src.y-2<=a.y<=src.y+2); */ { long Start = gv.getTime(); int i = 0;// 检查次数变量 Point src = this.src; ShortWay.pointbfs(src);// 在抢单时计算路径长度 while (gv.getTime() - Start <= 7500) { for (i = 0; i < amount; i++) { Taxi nowTaxi = Alltaxi.All.get(i); // System.out.println("检查出租车" + nowTaxi.getNum() + "能否接单"); int x = nowTaxi.getLoc_i(); int y = nowTaxi.getLoc_j(); // System.out.println("出租车" + nowTaxi.getNum() + "检查接单时位置" + "(" + x + "," + y + // ")"); if (0 <= x && x <= 80 && 0 <= y && y <= 80 && src.x - 2 <= x && x <= src.x + 2 && src.y - 2 <= y && y <= src.y + 2 && nowTaxi.getState() == 2) { // System.out.println(src.i); // System.out.println(src.j); if (Taxi_flag[i] == false) { Catch.add(nowTaxi); nowTaxi.addCredit(1); Taxi_flag[i] = true; System.out.println("将出租车" + nowTaxi.getNum() + "加入抢单队列"); } } } gv.stay(250); } }
程序存在的bug
本次程序的LoadFilename这项功能好像写的不是很好,出现了许多奇怪的bug,之后本人在第十次作业的时候重写了LoadFilename的功能使之完成了其功能。在这里简单叙述一下被调出的bug。
1、在判断指令是否符合要求时,本人一不小心将地图上路径点的坐标范围写成了0<=&&<=80(实际地图点是0<=&&<=79)。被报了3个bug,痛失分数,在这种小地方一定要注意,且边界条件要多多测试。
2、还有一处是关于指导书理解的bug,LoadFilename中初始化出租车的状态为接乘客或服务状态时,处理自定义。我将其处理为全部默认为等待状态(其实我是以偷懒的故意这么做的)。结果被报了一个Incomplete的bug,看来还是不能偷懒。
3、其余的bug均为LoadFilename没有处理好,我找了许久没能找到问题,就重写解决了。在此处不再赘述。
2、第十次作业
这次作业比较幸运,没有被挑出bug(可能是上次被扣掉的分数有些多,导致掉段了吧)。但是这不代表本次作业没有问题,相反这次作业仍存在着众多问题。我会在后面前置条件与后置条件的例子分析中指出,并改正。
3、第十一次作业
这次的作业JSF未被挑出bug,但是如同上次作业一样,并不代表没有问题。相反也存在许多不尽人意的地方。同样我会在后面指出并修改。
三、规格bug产生的原因
主要是不太会写,虽然这个理由很笼统,但确实是这样。在经过了几次OO上机课之后,接触了一些例子,感觉JSF的书写才规范了起来。所有我觉得主要原因是之前接触这些东西比较少,需要一些实例的说明才能写好吧。
四、写法改进
实例1
public boolean checksame(Req r) /** * @EFFECTS:(\all Req r;r match;\result == true); * (\all Req r;r not match;\result == false); */ { if (ReqTray.isEmpty()) { ReqTray.add(r); return true; } else if (ReqTray.get(ReqTray.size() - 1).getTime() == r.getTime()) { for (Req now : ReqTray) { if (now.equals(r)) { System.out.println("指令"+r.getReqnum()+"同质"); return false; } } ReqTray.add(r); return true; } else { ReqTray.clear(); ReqTray.add(r); return true; }
存在问题:REQUIRES根本没有,其实本方法是需要前置条件的,后置条件接近自然语言,改为逻辑表达式会更好一些。
public boolean checksame(Req r) /** * @REQUIRES: r != null; * @MODIFIES: ReqTray; * @EFFECTS: (\result == false)==>(\old(\this).contains(r) == true); * (\result == true)==>(\this.size == \old(\this).size+1) && (\this.contains(a) == true)&&(\result == true); */ { if (ReqTray.isEmpty()) { ReqTray.add(r); return true; } else if (ReqTray.get(ReqTray.size() - 1).getTime() == r.getTime()) { for (Req now : ReqTray) { if (now.equals(r)) { System.out.println("指令"+r.getReqnum()+"同质"); return false; } } ReqTray.add(r); return true; } else { ReqTray.clear(); ReqTray.add(r); return true; } }
实例2
public synchronized void addTaxi(int amount) /** * @MODIFIES:ArrayList<Taxi> Taxi; * @EFFECTS:(\all Taxi e;0 <= x && x <= 80 && 0 <= y && y <= 80 && src.x - 2 <=x && x <= src.x + 2 && src.y - 2 <= y && y <= src.y + 2 && nowTaxi.getState() == 2;Taxi.add(e)) */ { long Start = gv.getTime(); int i = 0;// 检查次数变量 Point src = this.src; ShortWay.pointbfs(src);// 在抢单时计算路径长度 while (gv.getTime() - Start <= 7500) { for (i = 0; i < amount; i++) { Taxi nowTaxi = Alltaxi.All.get(i); // System.out.println("检查出租车" + nowTaxi.getNum() + "能否接单"); int x = nowTaxi.getLoc_i(); int y = nowTaxi.getLoc_j(); // System.out.println("出租车" + nowTaxi.getNum() + "检查接单时位置" + "(" + x + "," + y + // ")"); if (0 <= x && x <= 80 && 0 <= y && y <= 80 && src.x - 2 <= x && x <= src.x + 2 && src.y - 2 <= y && y <= src.y + 2 && nowTaxi.getState() == 2) { // System.out.println(src.i); // System.out.println(src.j); if (Taxi_flag[i] == false) { Catch.add(nowTaxi); nowTaxi.addCredit(1); Taxi_flag[i] = true; System.out.println("将出租车" + nowTaxi.getNum() + "加入抢单队列"); } } } gv.stay(250); } }
存在问题:依旧是没有前置条件,后置条件为自然语言。
public synchronized void addTaxi(int amount) /** * @REQUIRES: amount >= 0; * @MODIFIES: Taxi; * @EFFECTS: (\all Taxi e;Catch.contain(e) == true) ==> (0<=a.x<=79 && src.x-2<=a.x<=src.x+2) * ||(0<=a.y<=79 && src.y-2<=a.y<=src.y+2); */ { long Start = gv.getTime(); int i = 0;// 检查次数变量 Point src = this.src; ShortWay.pointbfs(src);// 在抢单时计算路径长度 while (gv.getTime() - Start <= 7500) { for (i = 0; i < amount; i++) { Taxi nowTaxi = Alltaxi.All.get(i); // System.out.println("检查出租车" + nowTaxi.getNum() + "能否接单"); int x = nowTaxi.getLoc_i(); int y = nowTaxi.getLoc_j(); // System.out.println("出租车" + nowTaxi.getNum() + "检查接单时位置" + "(" + x + "," + y + // ")"); if (0 <= x && x <= 80 && 0 <= y && y <= 80 && src.x - 2 <= x && x <= src.x + 2 && src.y - 2 <= y && y <= src.y + 2 && nowTaxi.getState() == 2) { // System.out.println(src.i); // System.out.println(src.j); if (Taxi_flag[i] == false) { Catch.add(nowTaxi); nowTaxi.addCredit(1); Taxi_flag[i] = true; System.out.println("将出租车" + nowTaxi.getNum() + "加入抢单队列"); } } } gv.stay(250); } }
实例3
public void outputInfo(int Req_num) /** * @MODIFIES:None; * @EFFECTS:Output Info; */ { File file = new File("E:\\Bill"); if (!file.exists()) file.mkdirs(); FileWriter outputStream = null; try { outputStream = new FileWriter("E:\\Bill\\Check_Bill.txt", true); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); if (Req_num > this.getLength() - 1) { try { outputStream.write("不存在该订单"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } Bill now_Bill = this.Taxi_bill.get(Req_num); try { outputStream.write("出租车编号:" + this.Num + "\n"); outputStream.write("此为该出租车接到的第" + Req_num + "号单"); outputStream.write("本次账单号为:" + now_Bill.Reqnum + "\n"); outputStream.write("派单时出租车所在位置:" + "(" + now_Bill.Rec.x + "," + now_Bill.Rec.y + ")" + "\n"); outputStream.write("派单时刻:" + sdf.format(new Date(now_Bill.RecT)) + "\n"); outputStream.write("到达乘客位置时刻:" + sdf.format(new Date(now_Bill.StartT)) + "\n"); outputStream.write("乘客位置坐标:" + "(" + now_Bill.src.x + "," + now_Bill.src.y + ")" + "\n"); outputStream.write("到达目的地坐标:" + "(" + now_Bill.dst.x + "," + now_Bill.dst.y + ")" + "\n"); outputStream.write("到达目的地时刻:" + sdf.format(new Date(now_Bill.EndT)) + "\n"); } catch (IOException e) { // TODO Auto-generated catch block System.out.println("监控出租车输出错误"); e.printStackTrace(); } for(Bill_path path:this.Path) { if(path.getReq_num()==Req_num) { try { outputStream.write(path.getpath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
没有REQUIRES(我当时怎么想的就不写REQUIRES),因为输出是文件所以MODIFIES和EFFECTS应该是None吧。
public void outputInfo(int Req_num) /** * @REQUIRES: Req_num >= 0; * @MODIFIES: None; * @EFFECTS: None; */ { File file = new File("E:\\Bill"); if (!file.exists()) file.mkdirs(); FileWriter outputStream = null; try { outputStream = new FileWriter("E:\\Bill\\Check_Bill.txt", true); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); if (Req_num > this.getLength() - 1) { try { outputStream.write("不存在该订单"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } Bill now_Bill = this.Taxi_bill.get(Req_num); try { outputStream.write("出租车编号:" + this.Num + "\n"); outputStream.write("此为该出租车接到的第" + Req_num + "号单"); outputStream.write("本次账单号为:" + now_Bill.Reqnum + "\n"); outputStream.write("派单时出租车所在位置:" + "(" + now_Bill.Rec.x + "," + now_Bill.Rec.y + ")" + "\n"); outputStream.write("派单时刻:" + sdf.format(new Date(now_Bill.RecT)) + "\n"); outputStream.write("到达乘客位置时刻:" + sdf.format(new Date(now_Bill.StartT)) + "\n"); outputStream.write("乘客位置坐标:" + "(" + now_Bill.src.x + "," + now_Bill.src.y + ")" + "\n"); outputStream.write("到达目的地坐标:" + "(" + now_Bill.dst.x + "," + now_Bill.dst.y + ")" + "\n"); outputStream.write("到达目的地时刻:" + sdf.format(new Date(now_Bill.EndT)) + "\n"); } catch (IOException e) { // TODO Auto-generated catch block System.out.println("监控出租车输出错误"); e.printStackTrace(); } for(Bill_path path:this.Path) { if(path.getReq_num()==Req_num) { try { outputStream.write(path.getpath()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
实例4
public long getTime() /** * @MODIFIES:this.Time; * @EFFECTS:(\all long temp;temp >50) ==> \result == Time % 100 * 100 + 100; * (\all long temp;temp <= 50) ==> \result == Time % 100 * 100; */ { long temp = this.Time % 100; if (temp > 50) return Time % 100 * 100 + 100; else return Time % 100 * 100; }
public long getTime() /** * @REQUIRES: None; * @MODIFIES: this.Time; * @EFFECTS: (\all long temp;temp >50) ==> (\result == Time % 100 * 100 + 100); * (\all long temp;temp <= 50) ==> (\result == Time % 100 * 100); */ { long temp = this.Time % 100; if (temp > 50) return Time % 100 * 100 + 100; else return Time % 100 * 100; }
实例5
public synchronized Taxi FetchTaxi() /** * @REQUIRES:(ArrayList<Taxi> Taxi!=null); * @MODIFIES:ArrayList<Taxi> Taxi; * * @EFFECTS:\result==(\exist Taxi e;\max Taxi.Credit&&\min D[][]); */ { if (!Catch.isEmpty()) { int i = 1; Taxi now = Catch.get(0); while (i <= Catch.size() - 1 && !Catch.isEmpty()) { if (now.getCredit() < Catch.get(i).getCredit()) { int j = 0; for (j = i; j > 0; j--) Catch.remove(0); i = 0; now = Catch.get(i); } else if (now.getCredit() > Catch.get(i).getCredit()) { Catch.remove(i); } else if (now.getCredit() == Catch.get(i).getCredit()) { now = Catch.get(i); i++; } } i = 1; if (Catch.size() > 1) { now = Catch.get(0); while (i <= Catch.size() - 1 && !Catch.isEmpty()) { if (ShortWay.distance(now.getLoc_i(), now.getLoc_j(), src.x, src.y) > ShortWay .distance(Catch.get(i).getLoc_i(), Catch.get(i).getLoc_j(), src.x, src.y)) { int j = 0; for (j = i; j > 0; j--) Catch.remove(0); i = 0; now = Catch.get(i); } else if (ShortWay.distance(now.getLoc_i(), now.getLoc_j(), src.x, src.y) < ShortWay .distance(Catch.get(i).getLoc_i(), Catch.get(i).getLoc_j(), src.x, src.y)) { Catch.remove(i); } else if (ShortWay.distance(now.getLoc_i(), now.getLoc_j(), src.x, src.y) == ShortWay .distance(Catch.get(i).getLoc_i(), Catch.get(i).getLoc_j(), src.x, src.y)) { now = Catch.get(i); i++; } } } if (Catch.get(0).getState() == 2) { int Rondom = (int) (Math.random() * Catch.size()); Taxi Final = Catch.get(Rondom); Catch.clear(); return Final; } else { Catch.clear(); return null; } } else return null; }
修改REQUIRES与EFFECTS。
public synchronized Taxi FetchTaxi() /** * @REQUIRES: Catch != null; * @MODIFIES: Catch; * @EFFECTS: (\result == null) ==> (Catch.isEmpty() == true); */ { if (!Catch.isEmpty()) { int i = 1; Taxi now = Catch.get(0); while (i <= Catch.size() - 1 && !Catch.isEmpty()) { if (now.getCredit() < Catch.get(i).getCredit()) { int j = 0; for (j = i; j > 0; j--) Catch.remove(0); i = 0; now = Catch.get(i); } else if (now.getCredit() > Catch.get(i).getCredit()) { Catch.remove(i); } else if (now.getCredit() == Catch.get(i).getCredit()) { now = Catch.get(i); i++; } } i = 1; if (Catch.size() > 1) { now = Catch.get(0); while (i <= Catch.size() - 1 && !Catch.isEmpty()) { if (ShortWay.distance(now.getLoc_i(), now.getLoc_j(), src.x, src.y) > ShortWay .distance(Catch.get(i).getLoc_i(), Catch.get(i).getLoc_j(), src.x, src.y)) { int j = 0; for (j = i; j > 0; j--) Catch.remove(0); i = 0; now = Catch.get(i); } else if (ShortWay.distance(now.getLoc_i(), now.getLoc_j(), src.x, src.y) < ShortWay .distance(Catch.get(i).getLoc_i(), Catch.get(i).getLoc_j(), src.x, src.y)) { Catch.remove(i); } else if (ShortWay.distance(now.getLoc_i(), now.getLoc_j(), src.x, src.y) == ShortWay .distance(Catch.get(i).getLoc_i(), Catch.get(i).getLoc_j(), src.x, src.y)) { now = Catch.get(i); i++; } } } if (Catch.get(0).getState() == 2) { int Rondom = (int) (Math.random() * Catch.size()); Taxi Final = Catch.get(Rondom); Catch.clear(); return Final; } else { Catch.clear(); return null; } } else return null; }
五、bug聚合关系
第九次作业
方法名:InputThread;功能bug:2;规格bug:0;
LoadFilename;功能bug:3;规格bug:0;
第十次作业
方法名:无;功能bug:无;规格bug:无;
第十一次作业
方法名:Req;功能bug:2;规格bug:0;
看起来功能bug和规格bug关系不大,但事实上规格可以帮助我们确定边界条件,使我们的程序更不容易出错。
六、思路与体会
首先OO课程的困难阶段可能算是过去了吧。回想一下,虽然过程很累,但是还是有不少收获,至少push着我们学了不少的知识。关于JSF的问题,我已经看到不少同学在吐槽这个课程设置了,虽然我在编写程序的时候也没有体会到它的特殊作用。但是,我们应该意识到一个事实,规范化设计已经存在且蓬勃发展了这么长时间,那就一定有它的优势所在。可能因为我们还没有上升到认识到其重要性的高度,所以做这些事情的时候,只是觉得浪费时间。但我觉得学习这部分的知识还是有必要的。

浙公网安备 33010602011771号