博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
十分细微的收获
阅读量:6991 次
发布时间:2019-06-27

本文共 5630 字,大约阅读时间需要 18 分钟。

No way to no bug?

规格化设计的大致发展历史

从一位大师的演讲谈起:

The major cause of the software crisis is that the machines have become several orders of magnitude more powerful! To put it quite bluntly: as long as there were no machines, programming was no problem at all; when we had a few weak computers, programming became a mild problem, and now we have gigantic computers, programming has become an equally gigantic problem.— Edsger Dijkstra, The Humble Programmer (EWD340), Communications of the ACM

在我看来,他的意思是“编程”的复杂度随着计算机性能的提升和需求的提升而增加了。复杂度提高意味着“单人作战”和“闪电战”不再适应当时的需求,势必需要一种体系来支持“团队协作”和“持久战”。这在过去科技发展的进程中并非没有类似经验:产品的生产如何从手工作坊制造到工场集中生产再到工业化生产的?规格化,标准化是其中重要的推动力。编程领域的发展也是这样,需要规格化设计来支撑起“工业级别”的编码工程。

支持这一说法的证据之一是:有“一揽子”软件工程失败了,这些工程复杂度高投入人力物力大,但却依然以失败告终。

Started Terminated System name cost
1980s 1993 TAURUS £75m
1984 1990 RISP £63m
1997 2000 Bolit $35m

虽然导致软件项目失败的因素较为复杂,但从工程角度而言,没有进行规格化设计是关键因素之一。这说明有需要“革新”的需求。

于是软件工程开始成为人们研究的重点,人们期望研究出如何用系统化、规范化、数量化等工程原则和方法去进行软件的开发和维护,以降低软件工程的失败率。
期间出现和很多理论和优秀的工程语言,而发展到现在,出现了JSF这样的规格设计要求。 但,很不幸,现在依然有不少以失败告终的软件工程项目。

作业规格分析

规格BUG

  • THREAD_EFFECTS
    容易遗漏synchronized方法的lock()规格。

没有其它规格问题被发现。

规格 bug 产生的原因

完成工程的顺序错了。应当先设计架构,细化为规格,在实现具体的代码。否则在代码实现后人工撰写规格似乎意义不大——这项工作可以用自动化的方式完成。

在构建完代码后再研究规格,容易产生:先入为主,想当然等思维定势。似乎这样的方法在“利用”人的思维缺陷,而非帮助人避开这些问题。
之后的代码构建过程中应当注意设计和代码构建的顺序。

前置条件

``` * @REQUIRES: graphMatrix!=null; taxiGUI!=null; 0<=ID<100; time>0; _lights!=null; taxiCAL!=null; * graphMatrix.length==80; * (\all int i;0<=79; graphMatrix[i].length==80); * _lights.repOK() == true; * _taxiGUI.repOK() == true; * taxiCAL.repOK() == true;```1. 先写基本需求。2. 分行列详细需求。3. 利用repOK()。4. 限定严格的边界条件。5. 使用逻辑表达式

后置条件

```* (\this.type == rq.type && rq.startPosition == \this.startPosition  && rq.endPosition == this.endPosition && rq.startWindowTime == this.startWindowTime)==>\result == true; * (!(\this.type == rq.type && rq.startPosition == \this.startPosition  && rq.endPosition == this.endPosition && rq.startWindowTime == this.startWindowTime))==>\result == false; ```1. 构建代码运行分支。2. 按分支构建前件与后件。3. 考虑异常情况。4. 使用`==`。5. 不过于纠结如何用逻辑表达式表示函数调用。

基本思路和体会

JSF写得越接近纯bool表达式越好,在没有自动化逻辑检测工具的情况下,这样的方式在互测中比较占优势。

写在后面

LSP设计原则

本系统中涉及到继承的主要有两类:Request和Taxi。

注意到这两个类的主要的public方法在重载时都不改变原有方法的EFFECTS,而是在原有方法的基础上进行扩展,这使得外部调用者在调用子类public方法时得到的效果包含了调用父类相同方法的效果,这就使得外部调用任何父类出现的地方都可以使用子类来代替,并不会导致使用相应类的程序出现错误。
在实际实现的过程中也是以父类来统一管理不同的子类对象的,如TaxiHandler中的taxiListTaxi数组,但却管理了SutaxiTaxi

举例 Sutaxi -> Taxi

  • public synchronized boolean setRequest (Request rq, TaxiStatusPackage p)
public synchronized boolean setRequest(Request rq, TaxiStatusPackage p) {             if(super.setRequest(rq, p) == true) {            curRequestPackage = new ArrayList
(); Request temp = rq.clone(); requestHistory.add(temp ); rqToTSPListMap.put(temp.toString()+","+rq.startWindowTime, curRequestPackage); return true; }else return false; }

可见,其返回值相关的约束条件与父类相同,但是增添了一些SuTaxi本身所需的功能。不影响Taxi约束。

接口

如何保证类的封装行驶一个很复杂的问题,这里讨论一种特殊情况的解决方式:在某个类执行完某些请求后其他类要执行后续步骤,但其他类又不应属于执行某请求的类。

例如:
出租车在更新位置之后要让请求知道其是否进入了请求抢单范围,而在我的设计中,出租车不应该负责与请求的这类交互操作。如何解决?——接口。
代码样例如下:

  • TaxiHandler
//初始化出租车列表时for (int i=0;i<=29;i++) {            TaxiCAL tempTC = new TaxiCAL(map, taxiGUI.getOriGraphMatrix(), taxiGUI);            tempTC.setGraph(1);            taxiList[i] = new SuTaxi(i, startTime, matrixMap, tempTC, _lights, _taxiGUI)   //接口写法, 保证了封装性。。                    {                        @Override                        /**                         * @REQUIRES: e!=null; curTime>0;                          * @MODIFIES: taxiIDMap; this.curRequestPackage; taxiGUI;                         * @EFFECTS:                         * (\all Request rq; \old(taxiIDMap).containsKey(rq); rq.isWindowTime(curTime)&&rq.isNear(e.point)) ==>(call \this.getBill())&&(taxiIDMap.get(rq).contains(e.ID))                         * (e.statusCode == 1 || e.statusCode == 3) ==> \this.curRequestPackage.contains(e)                         * @THREAD_EFFECTS:                          * \lock(rwl.readLock())  // 消极锁保证效率                         */                        public void afterMove(TaxiStatusPackage e, long curTime) { //在这里可以直接调用TaxiHandler的成员变量,而this.object则调用了Taxi对象的成员变量(方法同理)                            if(e.statusCode==2) {                                rwl.readLock().lock();                                try {                                    for(Map.Entry
> entry : taxiIDMap.entrySet()) { Request iter = entry.getKey(); if(iter.isWindowTime(curTime)&&iter.isNear(e.point)) { if(!entry.getValue().contains(e.ID)) { this.getBill(); } entry.getValue().add(e.ID); } } }finally { rwl.readLock().unlock(); } }else if(e.statusCode == 1 || e.statusCode == 3) { this.curRequestPackage.add(e); } taxiGUI.SetTaxiStatus(e.ID, e.point, e.statusCode); } }; this.taxiGUI.SetTaxiType(i, 1); }

Taxi中预留了空接口:

/**    * @REQUIRES: NONE    * @MODIFIES: NONE    * @EFFECTS:  NONE    */public void afterMove(TaxiStatusPackage e, long curTime) {    }

转载于:https://www.cnblogs.com/neolinsu/p/9112989.html

你可能感兴趣的文章
Spring Cloud构建微服务架构:分布式服务跟踪(入门)【Dalston版】
查看>>
dubbo-rpc基本功能
查看>>
Ruby实现二分法查找
查看>>
第十二章 简单工厂模式(Simple Facotry)
查看>>
【Magedu】Week02
查看>>
python中asynchat异步socket命令/响应处理
查看>>
使用xcopy进行日增量备份
查看>>
知之者不如好之者,好之者不如乐之者
查看>>
测试Application.Idle
查看>>
web前端开发中浏览器兼容问题(五)
查看>>
我的友情链接
查看>>
QT创建与QT无关的纯C++程序和动态/静态库
查看>>
并查集(Union-Find)算法介绍
查看>>
MySQL主从配置
查看>>
Shell脚本介绍(资源)
查看>>
如何点击每一列的时候alert其index
查看>>
大型网站架构演变和知识体系
查看>>
DITA vs DocBook
查看>>
mysqlbinlog 使用
查看>>
RHCS中GFS2共享存储扩容
查看>>