1, 规格化设计的发展历程git
关于规格化设计一词,能找到的资料实在少之又少。在笔者已经找到的资料中,笔者认为,规格化设计来自于历史上第一次软件危机以后,虽然当时已经有了大量的面向过程式的编程语言譬如COBOL,BASIC,C语言等等,可是其中含有的大量的goto语句致使的面条式代码极大地限制了程序规模。程序员
为了解决如上的问题,人们提出告终构化程序设计这一中编程范型。它采用子程序(函数就是一种子程序)、代码区块、for循环以及while循环等结构,来替换传统的goto。但愿借此来改善计算机程序的明晰性、质量以及开发时间,而且避免写出面条式代码。编程
可是,因为硬件的飞速发展,业务的需求愈来愈复杂,爆发了第二次软件危机。这次危机主要体如今“可扩展性”,“可维护性”两个方面。因为传统面向过程式的编程难以适应快速多变的业务需求,于是产生了面向对象式的编程思想。在笔者看来,规格化设计一词,是在结构化设计的基础上,为了适应程序的“可扩展性”和“可扩展性”而衍生出来的重要设计思想。app
一个通过优秀的规格化设计的程序,有着以下的优点:其一,有助于开发者们相互理解对方的程序,方便程序员之间的分工,加快开发速度。现在的工程,每每都是由一个团队共同完成。那么对于团队中的成员来讲,互相理解对方的代码是十分重要的,这样才能更好地完成某个项目。其次,对于编写函数库的程序员来讲规格化设计更为重要。其缘由就在于函数库的使用者并不须要知晓库程序员的代码思路与实现过程,他只须要知道库中的某个函数,它须要的参数是什么,会对什么数据进行修改,修改的效果是什么,也就是咱们JSF中的REQUIRES,MODIFIES,EFFECTS三个重要的功能。其二,规格化的设计能很好地提升程序的“可扩展性”与“可维护性”。当开发者清楚本身所写的每个方法的规格时,他可以很快速地针对每个bug追本溯源,找出问题所在。其次,在面对现在工程需求快速变化的状况时,开发者经过规格化的设计能够很好地了解本身的程序应该如何进行修改以适应需求的变化。编程语言
2,三次做业的规格BUG函数
笔者的后三次出租车的做业并无在规格上被测试者报告BUG,但这并不意味着笔者的规格便没有任何的错误,仅仅是遇到了“心地善良”的测试者罢了。测试
那么笔者便来本身细数本身在规格上出现的纰漏以及修改的方案。spa
3,JSF不规范的写法以及改进设计
1)code
1 /** 2 * @REQUIRES:(\all String str;str != null,path != null) 3 * @MODIFIES:fw 4 * @EFFECTS: 5 * (\all str != null) ==> fw.write(str+"\n"); 6 */ 7 public static void appendFile(String Path,String str) { 8 try { 9 File f = new File(path); 10 FileWriter fw = new FileWriter(f, true); 11 fw.write(str + "\n"); 12 fw.close(); 13 } catch(Exception e) { 14 } 15 } 16 }
该方法的的做用显而易见,即是向文件中写入信息,那么该方法的JSF出了什么纰漏呢?
首先咱们对REQUIRES的定义是什么?它的定义是前置条件,也就是执行该方法前对该输入或者系统状态的要求。
该方法的前置条件是字符串str的含义是文件的路径,那么其前置条件即是str是合法的文件路径,而源代码中的前置条件仅仅强调了path != null,显然这并无知足要求。
而MODIFIES的定义反作用,是对input的修改,即对输入的修改。显而易见的是,本方法并无对输入进来的str进行任何修改,所以没法知足要求。
最后的EFFECTS的定义是后置条件,即执行后的返回结果或者系统状态应该知足的约束。本方法对系统形成的影响是往文件中写入字符串
所以对该规格的改进为:
1 1 /** 2 2 * @REQUIRES:(str!=null) && (path is a legitimate file path) 3 3 * @MODIFIES:none 4 4 * @EFFECTS: 5 5 * (\all str != null) ==> fw.write(str+"\n"); 6 6 */ 7 7 public static void appendFile(String Path,String str) { 8 8 try { 9 9 File f = new File(path); 10 10 FileWriter fw = new FileWriter(f, true); 11 11 fw.write(str + "\n"); 12 12 fw.close(); 13 13 } catch(Exception e) { 14 14 } 15 15 } 16 16 }
2)
1 /** 2 * @REQUIRES:Point p,q;int f; 3 * @MODIFIES:flow[][]; 4 * @EFFECTS: 5 * (isAdh(p,q) == false) ==> (/result == false); 6 * (isAdh(p,q) == true) ==> (/result == true)&&(flow[getPointId(p)][getPointId(q)] == flow[getPointId(p)][getPointId(q)]+f)&&(flow[getPointId(q)][getPointId(p)] == flow[getPointId(q)][getPointId(p)]+f); 7 */ 8 public boolean addFlow(Point p, Point q, int f) { 9 if(!isAdj(p, q)) { 10 return false; 11 } 12 synchronized(flow) { 13 flow[getPointId(p)][getPointId(q)] += f; 14 flow[getPointId(q)][getPointId(p)] += f; 15 } 16 return true; 17 }
该方法的REQUIRES不符合要求,应该要标明p,q和f的要求等状况
修改方法以下所示
/** * @REQUIRES:(Point p,q;p!=null,q!=null;int f,f > 0); * @MODIFIES:flow[][]; * @EFFECTS: * (isAdh(p,q) == false) ==> (/result == false); * (isAdh(p,q) == true) ==> (/result == true)&&(flow[getPointId(p)][getPointId(q)] == flow[getPointId(p)][getPointId(q)]+f)&&(flow[getPointId(q)][getPointId(p)] == flow[getPointId(q)][getPointId(p)]+f); */ public boolean addFlow(Point p, Point q, int f) { if(!isAdj(p, q)) { return false; } synchronized(flow) { flow[getPointId(p)][getPointId(q)] += f; flow[getPointId(q)][getPointId(p)] += f; } return true; }
3)
/** * @MODIFIES: None * @EFFECTS: /result == dist[getPointId(q)]; */ public int getDistance(Point p, Point q) { int[] dist = new int[map_size * map_size]; int[] minFlow = new int[map_size * map_size]; Point[] prev = new Point[map_size * map_size]; minPath(p, q, dist, minFlow, prev); return dist[getPointId(q)]; }
本方法没有写清楚前置条件和反作用
该方法并不只仅是得到p,q两点之间的距离,它为了获得这个距离寻找了两点之间的最短路径,所以会有必定的反作用。
修改方法以下所示:
1 /** 2 *REQUIRES:(Point p,q;p != null,q != null);
* @MODIFIES: minPath(p, q, dist, minFlow, prev); 3 * @EFFECTS: /result == dist[getPointId(q)]; 4 */ 5 public int getDistance(Point p, Point q) { 6 int[] dist = new int[map_size * map_size]; 7 int[] minFlow = new int[map_size * map_size]; 8 Point[] prev = new Point[map_size * map_size]; 9 minPath(p, q, dist, minFlow, prev); 10 return dist[getPointId(q)]; 11 }
4,功能BUG与规格BUG的聚焦分析
笔者的功能BUG与规格的BUG之间并无太大的联系,可能缘由在于笔者是在写完整个程序以后方才写的规格,并且因为部分方法所完成的功能太多,行数过长,于是致使JSF难以完成,所以这部分的JSF笔者并无很好地去完善,测试者也并无认真地去追究这些问题。
3,设计规格和撰写规格的基本思路和体会
笔者在完成这三次做业的时候,是先写完整个程序以后再去写的每一个方法和类的规格,仔细分析,有好有坏,可是弊大于利。其好处就在于写程序的时候不须要考虑的规格的问题,于是能提升速度。可是缺点也很明显,因为没有事先设计好规格,没有仔细明确每一个方法的要求,目的,以及反作用,在后期DEBUG的时候会形成巨大的困扰——不知道到底是哪一个类,哪一个方法的问题。所以,痛定思痛事后,仍是应该养成先肯定好每一个类的目标,即Overview,再肯定为了完成这些目标所须要的方法,肯定其REQUIRES,MODIFIES,EFFECTS,而后再依据本身的规格去实现本身的代码。我想这样的话可以减小很多DEBUG的时间,也能达到规格化训练的目的。更加剧要的是,这样作能够更加方便测试者理解而且测试你的程序,利己利人,何乐而不为呢?
以上即是笔者对于这三次做业的拙见,OO这门课即将到达尾声,一路走来经历了太多,正如那句耳熟能详的话语:OO不易,和谐6系。为了完成做业常常熬夜通宵什么的确实付出了不少,但一样的是,咱们收获的,也不少!