接前文:http://www.cnblogs.com/Kassadin/p/4091040.html

1.Before We Start:


在开始图书管理系统需求定义之前,需要先进行一些说明。

1.1 输入,输出定义

输入:用户需求文字说明

输出:基于VDM++的需求规格说明文档

任何问题只有明确它的输入和输出,才会有一个明确的预期,才有可能获得预期的结果。在这里明确问题的输入输出更加重要。特别需要指出的是,VDM++作为一种形式化方法语言,它主要用于需求分析,而不是代码实现。虽然它的产出是一段一段的代码,但如果你期望通过VDM++得到现成的系统实现代码,很抱歉,我们无法得到预期的输出。

VDM++不是Java这类系统实现的语言,它的输出结果不是Web网站,不是GUI程序,甚至连命令行程序都实现不了。想到这些,难免有些失落,总觉得失去了编程的那种成就感;但这就是事实,这或许也是形式化方法难于普及的原因之一。

1.2 VDM理论探讨

那么,VDM++的输出是什么呢?如前文所述,其输出是需求规格说明书(需求文档),编写需求文档可以采用以下几种描述方法:

a.自然语言描述(最通俗易懂的方法,但不标准,歧义较多)

b.结构化系统分析方法——流程图,数据流图,数据字典等(上世纪70年代结构化思想的产物,与自然语言相比更加规范,更加接近计算机语言)

c.OOD方法——UML建模(上世纪80年代面向对象思想的产物,时至今日仍然有很大的影响力,特点是半结构化,需求描述规范清晰,可通过一定的框架转化为实际代码,仍然是主流的系统分析与设计技术)

d.形式化方法——VDM,Z方法等(上世纪90年代开始逐渐发展起来的软件工程方法,采用类似于编程语言的代码形式描述软件需求,特点是完全结构化,形式化,自动化)

由此可见,如果要在软件工程中找到一个可以和VDM类比的东西的话,我个人认为应该是UML;或许我们应该把VDM++的代码理解为各种各样的用例图,活动图,类图。形式化方法的主要作用领域是在软件开发中的上游工程(即需求,设计,自动化测试等),而不是具体的编码实现。

这些许令人有些失望,辛辛苦苦写出来那么多的VDM代码,居然不能作为系统实现的代码;那我为什么还要写这些东西呢?

1.3 VDM++的作用范围

这里,我详细阐述一下VDM++能做些什么!?具体请参考该网站:http://monoist.atmarkit.co.jp/mn/articles/0810/20/news106.html

1.编写需求规格说明书,检查需求分析结果和用户需求的一致性

2.检查需求规格说明书,系统设计文档的正确性

3.检查需求规格说明书,系统设计文档的一致性

4.为下游工程提供自动化测试用例

通过形式化方法,最终的理想目标是实现软件工程化,自动化,精确化,标准化;实际结果是减少需求工程中的不一致性(如下图),降低软件开发的时间(人月),提高软件开发效率,保证系统开发结果与客户需求相一致。

上图形象地阐释了客户需求和软件最终产品的鸿沟,已成为软件工程经典案例,这里不再赘述。下图解释了形式化方法与软件工程的关系:

2.Now Let us Start!


理论说明了一大堆,现在终于可以开始实战了!如何实现从用户需求到VDM++的转化呢!?

2.1 用户需求

1.开发一个计算机系统用于管理图书的借还

2.实现用户管理

3.实现图书管理

4.用户可以进行图书借还

为了简化问题,这里的需求非常的简单——只有4句话,现在我们要做的就是细化这些需求。

2.2 需求的细化

(1)首先我们明确我们需要开发一个图书管理系统(Library System)

(2)其次,我们抽取出系统实体和系统参与者的活动(和用例图建模一样)

系统实体:Library,User,Book

活动:borrow books,return books,manage users,manage books

(3)抽取相应的操作

borrow_book,return_book,add_user,remove_user,add_book,remove_book

2.3 开始编码

【VDM++的语法还是比较多的,这里我们不能纠缠于VDM的语法细节,这些语法细节会在之后慢慢解释】

2.3.1 建立项目

(1)打开Overture Tool,新建Project

(2)输入Project Name

(3)选择导入IO Library

2.3.2 新建Library文件

在项目上点击右键,New->Empty VDMPP File,输入文件名

2.3.3 定义必要的类和数据类型(代码后附)

2.3.4 定义操作operations

需要注意的是,由于VDM的主要作用在于需求描述,因此在VDM++中操作体不是最重要的要素,可以先不定义,在这里,我就用了is not yet specified把操作体先留空。

2.3.5 定义操作的前置条件和后置条件

pre-condition和post-condition是大部分形式化方法最重要的特点!!他们定义了操作的前提和结果,通常我们会使用post条件进行结果检查与测试,他们的作用通常比操作体本身还要重要。

2.3.6 补全操作体

add_user方法操作体只有1行,即求sUsers和aUser的并集,合并后赋值给sUsers

2.3.7 补全instance variables

补充sBorrowing映射实例的定义域和值域(这对于map类型非常重要),其定义域为sCopies的子集,其值域为sUsers的子集;即把书借给用户

2.3.8 根据需求定义函数functions

这里由于多处用到了判断某本书是否被借走,为了避免重复,我们应该定义一个函数封装该功能。

2.3.9 代码编写完成,全部代码如下:

  1. class Library
  2. --类型定义
  3. types
  4. public Copy = token ;
  5. public User = token ;
  6. Borrowing = map Copy to User;
  7.  
  8. --实例定义和初始化
  9. instance variables
  10. sCopies : set of Copy := {};
  11. sUsers: set of User := {};
  12. sBorrowing: Borrowing := { |-> };
  13. --定义map类型实例的定义域和值域
  14. inv
  15. dom sBorrowing subset sCopies and
  16. rng sBorrowing subset sUsers;
  17.  
  18. operations
  19. --定义操作,操作名add_user,输入参数类型User,返回值为空
  20. public add_user : User ==> ()
  21. --定义操作体,此处还尚未定义
  22. add_user(aUser) ==
  23. sUsers := sUsers union {aUser}
  24. --定义操作的前置条件和后置条件
  25. --前置条件:aUser不在sUsers
  26. --后置条件:aUser加入了sUsers集合
  27. pre
  28. aUser not in set sUsers
  29. post
  30. sUsers = sUsers~ union {aUser};
  31.  
  32. public remove_user : User ==> ()
  33. remove_user(aUser) ==
  34. sUsers := sUsers \ {aUser}
  35. pre
  36. aUser in set sUsers and
  37. not borrowedUser(sBorrowing, aUser)
  38. post
  39. sUsers = sUsers~ \ {aUser};
  40.  
  41. public add_book : Copy ==> ()
  42. add_book(aCopy) ==
  43. sCopies := sCopies union {aCopy}
  44. pre
  45. aCopy not in set sCopies
  46. post
  47. sCopies = sCopies~ union {aCopy};
  48.  
  49. public remove_book: Copy ==> ()
  50. remove_book(aCopy) ==
  51. sCopies := sCopies \ {aCopy}
  52. pre
  53. aCopy in set sCopies and
  54. not borrowedCopy(sBorrowing, aCopy)
  55. post
  56. sCopies = sCopies~ \ {aCopy};
  57.  
  58. public borrow_book : User * Copy ==> ()
  59. borrow_book(aUser, aCopy) ==
  60. sBorrowing := sBorrowing munion {aCopy |-> aUser}
  61. pre
  62. aUser in set sUsers and
  63. aCopy in set sCopies and
  64. not borrowedCopy(sBorrowing, aCopy)
  65. post
  66. sBorrowing = sBorrowing~ munion {aCopy |-> aUser};
  67.  
  68. public return_book : Copy ==> ()
  69. return_book(aCopy) ==
  70. sBorrowing := {aCopy }<-: sBorrowing
  71. pre
  72. borrowedCopy(sBorrowing, aCopy)
  73. post
  74. sBorrowing = {aCopy } <-: sBorrowing~;
  75.  
  76. public getAttributes : () ==> set of Copy * set of User * map Copy to User
  77. getAttributes() == return mk_(sCopies, sUsers, sBorrowing);
  78.  
  79. functions
  80. --函数定义,该函数判断该书是否已经被借走,输入参数是Borrowing类型和Copy类型,如果被借走则返回true
  81. borrowedCopy : Borrowing * Copy +> bool
  82. borrowedCopy(aBorrowing, aCopy) ==
  83. aCopy in set dom aBorrowing;
  84.  
  85. borrowedUser : Borrowing * User +> bool
  86. borrowedUser(aBorrowing, aUser) ==
  87. aUser in set rng aBorrowing;
  88.  
  89. end Library

需要注意的是,现在没有必要纠缠于语法细节,对于每一部分的具体语法,会在后续文章陆续介绍。本次的关注点主要在于概览VDM++的开发过程,程序基本结构,基本概念等要素,而不是具体的语法细节。

2.4 编写测试类

程序编写完了,总需要看看运行结果吧;很显然现在这个程序没有运行结果,原因很简单,只有类定义,没有相应的测试数据。就好比Java程序,定义了一个类,但是没有写main函数一样,因此,我们必须编写测试类,测试所写的Library需求。

这里,我直接提供一个简单的测试类。

  1. class UseLibrary
  2.  
  3. instance variables
  4. sL : Library := new Library();
  5.  
  6. functions
  7. public run : () -> seq of char * bool * map nat1 to bool
  8. run() ==
  9. let testcases = [
  10. t1()
  11. ],
  12. testResults = makeOrderMap(testcases)
  13. in
  14. mk_("The result of regression test = ", forall i in set inds testcases & testcases(i), testResults);
  15.  
  16. static public makeOrderMap : seq of bool +> map nat1 to bool
  17. makeOrderMap(s) == {i |-> s(i) | i in set inds s}
  18. pre s <> [];
  19.  
  20. operations
  21. t1: () ==> bool
  22. t1() ==
  23. let l = new Library(),
  24. p = mk_token("Sakoh"),
  25. c = mk_token("OO Construction_1")
  26. in (
  27. l.add_user(p); l.add_book(c); l.borrow_book(p, c); l.return_book(c); l.remove_user(p);
  28. return
  29. l.getAttributes() = mk_({mk_token("OO Construction_1")}, {}, {|->})
  30. );
  31. end UseLibrary

该测试类添加了一个实例,l是一个Library实例,p是一个User实例,c是一个Copy实例,我们对l进行添加用户,添加图书,借书,还书,删除用户等一系列操作,最后比较l中的所有属性值与给定属性值mk_({mk_token("OO Construction_1")}, {}, {|->})是否相等,即判断是否符合预期。如果返回true,则该测试用例通过,需求定义合理;否则,该测试用例不通过,需求定义不符合要求。

2.5 测试结果

(1)修改run configuration

右键工程,Run As->run configurations->如图:

(2)点击运行,弹出如下窗口:

(3)输入print new UseLibrary().run()查看运行结果是否符合预期

结果显示为true,说明测试用例通过。

3.After Coding


至此,我们的图书管理系统需求定义基本完成(其实还有很多没有做,如测试覆盖率检查,需求文档生成等等)。VDM++已经向我们展示了它的强大功能。在后续部分,我会逐步解释VDM++的语法特征和具体用法,敬请期待。

最后来看看我们的VDM代码被转化成了哪些Java代码吧!

右键工程,Code Generation->Generate Java

  1. import org.overture.codegen.runtime.*;
  2.  
  3. import java.util.*;
  4.  
  5. public class Library {
  6. private VDMSet sCopies = SetUtil.set();
  7. private VDMSet sUsers = SetUtil.set();
  8. private VDMMap sBorrowing = MapUtil.map();
  9.  
  10. public Library() {
  11. }
  12.  
  13. public void add_user(final Token aUser) {
  14. sUsers = SetUtil.union(sUsers.clone(), SetUtil.set(aUser));
  15. }
  16.  
  17. public void remove_user(final Token aUser) {
  18. sUsers = SetUtil.diff(sUsers.clone(), SetUtil.set(aUser));
  19. }
  20.  
  21. public void add_book(final Token aCopy) {
  22. sCopies = SetUtil.union(sCopies.clone(), SetUtil.set(aCopy));
  23. }
  24.  
  25. public void remove_book(final Token aCopy) {
  26. sCopies = SetUtil.diff(sCopies.clone(), SetUtil.set(aCopy));
  27. }
  28.  
  29. public void borrow_book(final Token aUser, final Token aCopy) {
  30. sBorrowing = MapUtil.munion(sBorrowing.clone(),
  31. MapUtil.map(new Maplet(aCopy, aUser)));
  32. }
  33.  
  34. public void return_book(final Token aCopy) {
  35. sBorrowing = MapUtil.domResBy(SetUtil.set(aCopy), sBorrowing.clone());
  36. }
  37.  
  38. public Tuple getAttributes() {
  39. return Tuple.mk_(sCopies.clone(), sUsers.clone(), sBorrowing.clone());
  40. }
  41.  
  42. private static Boolean borrowedCopy(final VDMMap aBorrowing,
  43. final Token aCopy) {
  44. return MapUtil.dom(aBorrowing.clone()).contains(aCopy);
  45. }
  46.  
  47. private static Boolean borrowedUser(final VDMMap aBorrowing,
  48. final Token aUser) {
  49. return MapUtil.rng(aBorrowing.clone()).contains(aUser);
  50. }
  51. }

【形式化方法:VDM++系列】3.基于VDM++的图书管理系统需求定义的更多相关文章

  1. 基于web的图书管理系统设计与实现

    原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述     图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...

  2. 基于web的图书管理系统设计与实现(附演示地址)

    欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...

  3. 【形式化方法:VDM++系列】4.VDM实战1——铁路费用计算

    又有将近2个月没更新博客了啊!winter holiday简直玩儿疯了的说!结果假期前学习的形式化方法已经忘了大半!面对期末作业,大脑一片空白.于是,赶快复习了一下之前学习的姿势! 这次的主要任务是完 ...

  4. 【形式化方法:VDM++系列】2.VDMTools环境的搭建

    接前文:http://www.cnblogs.com/Kassadin/p/3975853.html 上次讲了软件需求分析的演化过程,本次进入正题——VDM开发环境的搭建 (自从发现能打游戏以来,居然 ...

  5. 【形式化方法:VDM++系列】1.前言

    1.前言 今天开始上课学习软件需求分析与VDM++,经过一节课的学习,我又增长了见识. 软件需求工程在软件工程中处于十分核心的地位:需求分析的好坏直接决定软件工程的成败.这一点是我之前对需求工程的理解 ...

  6. Chapter 5 软件工程中的形式化方法

    从广义上讲,形式化方法是指将离散数学的方法用于解决软件工程领域的问题,主要包括建立精确的数学模型以及对模型的分析活动.狭义的讲,形式化方法是运用形式化语言,进行形式化的规格描述.模型推理和验证的方法. ...

  7. 【文智背后的奥秘】系列篇——基于CRF的人名识别

    版权声明:本文由文智原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/133 来源:腾云阁 https://www.qclou ...

  8. Shone.Math开源系列1 — 基于.NET 5实现Math<T>泛型数值计算

    Shone.Math开源系列1 — 基于.NET 5实现Math<T>泛型数值计算 作者:Shone .NET 5 preview 4已经可用了,从微软Build2020给出的信息看,.N ...

  9. Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

随机推荐

  1. TTTAttributedLabel 如何将多个字符串高亮显示

    TTTAttributedLabel进行多个字符串的高亮显示. 需要对每个字符串进行匹配,从而得到所有需要高亮的NSRange,然后利用NSMutableAttributedString对每个NSRa ...

  2. Windows 右键添加「cmd 打开」

    1. 2. 3. 参考: 1.Windows右键添加"使用CMD打开" 2.WIN7.WIN8 右键在目录当前打开命令行Cmd窗口(图文)

  3. Linux进程调度

    原文地址: http://cchxm1978.blog.163.com/blog/static/35428253201092910491682/ 相当不错的文章,读了后收藏,多谢博主分享! ----- ...

  4. 使用UDP进行数据发送的实例一

    首先如果TCP学过以后,再看UDP进行数据传输也是大同小异的,只是用到的类不同 UDP进行传输需要DataSocket和Datapacket类,Datapacket叫数据报,每一个数据报不能大于64k ...

  5. mysql---用户管理

    #创建用户king , 登陆密码为1234 create user 'king' identified by '1234'; #查看创建用户的语句,即上面那条创建用户的语句 show grants f ...

  6. javascript 文本框中,判断回车键触发事件 兼容IE&FireFox

    1.onkeypress&onkeydown区别 onkeypress 事件在用户按下并放开任何字母数字键时发生.但是系统按钮(例如:箭头键.功能键)无法得到识别. onkeydown 事件在 ...

  7. js字符串长度计算(一个汉字==两个字符)和字符串截取

    js字符串长度计算(一个汉字==两个字符)和字符串截取 String.prototype.realLength = function() { return this.replace(/[^\x00-\ ...

  8. POJ 1001 解题报告 高精度大整数乘法模版

    题目是POJ1001 Exponentiation  虽然是小数的幂 最终还是转化为大整数的乘法 这道题要考虑的边界情况比较多 做这道题的时候,我分析了 网上的两个解题报告,发现都有错误,说明OJ对于 ...

  9. ThinkPHP的缓存 F方法

    一般使用文件方式的缓存就能够满足要求,而thinkphp还提供了一个专门用于文件方式的快速缓存方法f方法. 由于采用的是php返回方式,所以其效率较s方法较高. f方法具有如下特点: 1.简单数据缓存 ...

  10. Oracle中的CR块详解

    1.概述 Cr块consistent read块也就是用来维护oracle的读一致性的数据块.当查询某些数据的时候,发现数据块的版本比我们要查询的新,例如session1执行了dml操作并没有提交,s ...