欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

软件构造3.2设计规约笔记

程序员文章站 2022-07-14 21:15:47
...

Chapter3:抽象数据类型(ADT)和面向对象的编程(OOP)

3.2Designing specification设计规约

1.规约的作用(spec)

  • 规约给“供需双方”都确定了责任,在调用的时候双方都要遵守。
  • 规约可以隔离“变化”,无需通知客户端。

2.行为的等价性(Behavioral equivalence)

不同的函数行为不同,但对用户来说“是否等价”?
需要使用规约来判断是否是一致的。
我们来看一个例子。

判断两个函数的行为是否一致?
第一个函数从前向后搜索数值val的索引值。
第二个函数从后向前搜索。软件构造3.2设计规约笔记
函数的规约
软件构造3.2设计规约笔记
由于规约中,需求中val值只在数组中出现一次,所以find函数无论从前还是从后进行搜索,返回的值是相同的。所以行为等价。

注意:用户需要给合理的输入,如果输入不合理,输出任何值都是对的,行为也是等价的。

3.前置条件和后置条件(pre-condition and post condition)

  • 前置条件:requires(输入的描述)

  • 后置条件:effects(输出的描述)

  • 前置条件:是对客户端的约束,使用时必须满足条件

  • 后置条件:对开发者的约束,方法结束时必须满足的条件

  • 契约:如果前置条件满足,后置条件必须满足。前置条件不满足,方法可做任何事情均不违约。

4.Java中的规约写法

/**Find a value in an array.
*@param arr array to search,requires that val occurs exactly once in arr
*@param val value to search for
*@return index i such that arr[i]=val

*/
  1. 第一行中有两个*。
  2. @param描述输入的参数,@return描述输出的值。

下面是错误的规约写法
软件构造3.2设计规约笔记

5.改变输入的方法(a mutating method)

前两个例子改变了输入的参数,第三个例子返回了一个新的列表

函数功能:把list2的元素加在list1的后面(mutating)软件构造3.2设计规约笔记

函数功能:把list的元素排序(mutating)软件构造3.2设计规约笔记
函数功能:把首字母小写,返回一个新的list(not mutating)软件构造3.2设计规约笔记

注意:

  1. 除非在后置条件里声明过,否则内部不应该改变输入参数。
  2. 尽量不设计mutating的spec,否则容易引发bugs。
  3. 尽量避免使用mutable的对象。

6.规约的强弱比较

更强的规约:

  1. 前置条件更弱。(输入的范围更广)
  2. 后置条件更强。(输出的范围更小)

举三个例子:
例子1
原始规约
软件构造3.2设计规约笔记
输入更弱,val可以有多个值软件构造3.2设计规约笔记
输出更强,只能输出第一个下标软件构造3.2设计规约笔记
例子2
原始规约软件构造3.2设计规约笔记
可以随意输入,前置条件更弱了
而后置条件没有变化,在前置条件相同时,后置条件的输出不变软件构造3.2设计规约笔记
例子3
原始规约软件构造3.2设计规约笔记
前置条件更弱了,
然而后置条件也更弱了,导致无法比较。软件构造3.2设计规约笔记

更强的规约代表更多的客户端(more clients)能应用,而有更少的实现(fewer implementations)

7.确定的规约和欠定的规约(Deterministic and underdetermined spec)

  • 确定的规约:给定一个满足precondition的输入,输出是唯一的,明确的
  • 欠定的规约:同一个输入可以有多个输出

举例说明:
第一个是欠定的规约,而后三个是确定的规约。
软件构造3.2设计规约笔记

8.操作式规约和声明式规约(Declarative vs operational specs)

  • 操作式规约:例如伪代码。
  • 声明式规约:没有内部实现的描述,只有初-终状态。(更有价值)

举例判断哪个是声明式规约。
软件构造3.2设计规约笔记
结论:第三个是,前两个不是。
原因:第一个提到了输出的类型和参数,第二个提到了用循环来做,都提到了内部的实现。而第三个是描述,没有提到内部的实现。

相关标签: 软件构造