Transcript 接口规格说明
软件工程
Software Engineering
Fall, 2012
设计接口规格说明
在系统设计过程中所标识的子系统功能,都能
通过设计接口规格说明,在类接口中详细说明。
接口规格说明的目标是足够清晰地描述每一个
对象的接口,为实现这个对象打下基础。为此,
设计接口规格说明的主要活动包括:
1.
2.
3.
4.
标识遗漏的属性和操作
标识类型、可见性和签名
描述前置条件和后置条件
描述不变式
2
接口规格说明的概念
类实现者、类扩展者、类用户
类型、签名和可见性
契约:不变式、前置条件、后置条件
1.
2.
3.
契约是每一个操作在被调用之前所需满足的条件,
操作完成后所需满足的条件,包括返回结果需满
足的条件。
3
类实现者、类扩展者、类用户
类实现者-负责实现正在考虑中的类。
类实现者设计内部的数据结构,并为每个公共操作实现代
码。
对类实现者而言,接口规格说明是一个工作分配。
类扩展者-开发正在考虑中的类的指定扩展。
对类扩展者而言,“接口规格说明”说明了当前的类行为,
又说明了指定类提供服务的所有约束。
类用户-在其它类的实现过程中,调用正在考虑类
所提供的操作,也称为客户类。
对类用户而言,接口规格说明根据类提供的服务和对客户
类所做的假设,揭示了类的边界。
4
Developers play 3 different Roles
during Object Design of a Class
Class User
Developer
Class Implementor
Class Extender
Call the Class
Realize the Class
(Implement it)
Refine the Class
(Implement a
subclass)
5
类用户 vs. 类扩展者
负责实现League类
的开发者是Game的
类用户
负责实现Game类的开发者是
Game的类实现者
League
Game
1
*
Tournament
TicTacToe
负责实现TicTacToe
和Chess类的开发者
是Game的类扩展者
Chess
TicTacToe
6
三连棋游戏
类型、签名和可见性
• 可见性
• Class user (“Public”): +
公有的属性/操作可以被任何类访问
• Class implementor (“Private”): 私有的属性/操作可以被只能被定义它们的类访问;
不能被子类或其它类访问。
• Class extender (“Protected”): #
保护的属性/操作被定义它们的类和子类访问
• 在UML中,“+”表示public, “-”表示private, “#”表
示protected.
7
Java实现UML的可见性
Tournament
- maxNumPlayers: int
+
+
+
+
+
getMaxNumPlayers():int
getPlayers(): List
acceptPlayer(p:Player)
removePlayer(p:Player)
isPlayerAccepted(p:Player):boolean
public class Tournament {
private int maxNumPlayers;
public
public
public
public
public
public
Tournament(League l, int maxNumPlayers)
int getMaxNumPlayers() {…};
List getPlayers() {…};
void acceptPlayer(Player p) {…};
void removePlayer(Player p) {…};
boolean isPlayerAccepted(Player p) {…};
8
添加类型签名(Signature)信息
Hashtable
numElements:int
put()
get()
remove()
containsKey()
size()
Hashtable
需求分析时,属性
和操作没有可见性
和类型信息
-numElements:int
+put(key:Object,entry:Object)
+get(key:Object):Object
+remove(key:Object)
+containsKey(key:Object):boolean
+size():int
在对象设计时, 需添加:
•属性的数据类型
•操作的参数及其类型,返回结果9
的数据类型.
契约:不变式、前置条件、后置条
件
• Arena系统的契约例子:
一个已经注册的选手不能再注册了;
一个联赛(tournament )里的选手的数量不能大于能容
纳的最大数量(maxNumPlayers);
只有已经注册的选手才能被删除。
• 契约也可以采用UML建模。
• 对象约束语言,OCL(Object Constraint
Language, OCL),已经是UML标准的一部分了。
10
课堂练习
Stack的契约有哪些?
11
契约的UML表示法
契约包含3种约束:
不变式(Invariant):
前置条件(Precondition ):
对于一个类的所有实例都总是为“真”(true).
在一个操作调用之前必须为“真”.
后置条件(Postcondition):
一个操作调用之后必须为“真”.
12
契约的UML表示法
UML类图,契约可以用注释表示
用对象约束语言(OCL: Object Constraint
Language)表示。
13
UML模型表示契约
• 一个契约可以用一个注释表示,通过关联将契约附加在所
约束的元素上。
• 但是UML图表示契约会很混乱。
<<precondition>>
!containsKey(key)
HashTable
<<invariant>>
numElements >= 0
numElements:int
<<precondition>>
containsKey(key)
<<precondition>>
containsKey(key)
put(key,entry:Object)
<<postcondition>>
get(key):Object
get(key) == entry
remove(key:Object)
containsKey(key:Object):boolean
size():int
<<postcondition>>
!containsKey(key)
14
课堂练习
用UML模型表示Stack的契约
15
OCL:
Object Constraint Language
可以选用文本形式描述OCL表达式。
OCL是表示契约的一种语言。
OCL的基本概念
OCL表达式-返回“真”或“假”(True or False);
16
OCL Simple Predicates
例如:
context Tournament inv:
self.getMaxNumPlayers() > 0
OCL表达式
意思:
“The maximum number of players in any
tournament should be a positive number.”
注意:
“context”-关键字,表明该表达式所适用的实体。
“self”-表示Tournament类的所有的实例
“.”-. 操作符
17
OCL前置条件
例如:
context Tournament::acceptPlayer(p) pre:
not self.isPlayerAccepted(p)
意思:
“The acceptPlayer(p) operation can only be invoked if
player p has not yet been accepted in the tournament.”
注意:
这个前置条件的context是一个操作;
isPlayerAccepted(p)
是Tournament类的一个操作。
pre-表示前置条件。
18
OCL后置条件
例如:
context Tournament::acceptPlayer(p) post:
self.getNumPlayers() =
[email protected]() + 1
意思:
“The number of accepted player in a tournament increases
by one after the completion of acceptPlayer()”
注意:
self@pre-表示tournament在操作调用之前的状态。
self-表示
tournament在操作完成后的状态。
post-表示后置条件。
19
Tournament 类中acceptPlayer()
方法的OCL契约
context Tournament::acceptPlayer(p) pre:
not isPlayerAccepted(p)
context Tournament::acceptPlayer(p) pre:
getNumPlayers() < getMaxNumPlayers()
context Tournament::acceptPlayer(p) post:
isPlayerAccepted(p)
context Tournament::acceptPlayer(p) post:
getNumPlayers() = @pre.getNumPlayers() + 1
20
Tournament 类中
removePlayer()方法的OCL契约
context Tournament::removePlayer(p) pre:
isPlayerAccepted(p)
context Tournament::removePlayer(p) post:
not isPlayerAccepted(p)
context Tournament::removePlayer(p) post:
getNumPlayers() = @pre.getNumPlayers() - 1
21
Tournament 类的Java代码实现
(契约是Java文档集合)
public class Tournament {
/** The maximum number of players
* is positive at all times.
* @invariant maxNumPlayers > 0
*/
private int maxNumPlayers;
/** The players List contains
* references to Players who are
* are registered with the
* Tournament. */
private List players;
/** Returns the current number of
* players in the tournament. */
public int getNumPlayers() {…}
/** Returns the maximum number of
* players in the tournament. */
public int getMaxNumPlayers() {…}
/** The acceptPlayer() operation
* assumes that the specified
* player has not been accepted
* in the Tournament yet.
* @pre !isPlayerAccepted(p)
* @pre getNumPlayers()<maxNumPlayers
* @post isPlayerAccepted(p)
* @post getNumPlayers() =
*
@pre.getNumPlayers() + 1
*/
public void acceptPlayer (Player p) {…}
/** The removePlayer() operation
* assumes that the specified player
* is currently in the Tournament.
* @pre isPlayerAccepted(p)
* @post !isPlayerAccepted(p)
* @post getNumPlayers() =
*
@pre.getNumPlayers() - 1
*/
public void removePlayer(Player p) {…}
}
22
maxNumPlayers
/** The maximum number of players
* is positive at all times.
* @invariant maxNumPlayers > 0
*/
private int maxNumPlayers;
23
players
/** The players List contains
* references to Players who are
* are registered with the
* Tournament. */
private List players;
24
/** Returns the current number of
* players in the tournament. */
public int getNumPlayers() {…}
/** Returns the maximum number of
* players in the tournament. */
public int getMaxNumPlayers() {…}
25
acceptPlayer (Player p)
/** The acceptPlayer() operation
* assumes that the specified
* player has not been accepted
* in the Tournament yet.
* @pre !isPlayerAccepted(p)
* @pre getNumPlayers()<maxNumPlayers
* @post isPlayerAccepted(p)
* @post getNumPlayers() =
* @pre.getNumPlayers() + 1
*/
public void acceptPlayer (Player p) {…}
26
removePlayer(Player p)
/** The removePlayer() operation
* assumes that the specified player
* is currently in the Tournament.
* @pre isPlayerAccepted(p)
* @post !isPlayerAccepted(p)
* @post getNumPlayers() =
* @pre.getNumPlayers() - 1
*/
public void removePlayer(Player p) {…}
27
1. 标识遗漏的属性和操作
检查每一个子系统提供的服务以及每一个分析
对象;
标识需要子系统服务实现,但却遗漏的属性和
操作。为此对当前对象设计模型细化,对所用
到的操作参数细化。
28
2. 标识类型、可见性和签名
确定哪些操作可以提供给外部其他对象或子系统
使用,哪些操作只能属于同一类的对象。(可见性)
还要说明每一个操作的返回类型、参数的数目和
类型。(签名)
本活动的目标是建立并提供一个较小且简单的接
口,便于使用者理解和使用;同时降低子系统之
间的耦合度,便于系统的更新。
29
3. 描述前置条件和后置条件
为每个类的所有公有操作订立契约,即存在于类
使用者和类实现者之间的一个协议。
通过前置条件描述类使用者必须遵守的那部分契
约,即操作正确执行必须满足的先决条件;
后置条件描述类使用者在遵守相关部分契约之后,
类实现者应当保证相关操作能够正确执行,并在
执行之后契约得到满足。
30
4. 描述不变式
不变式是一种与类和接口有关的约束,通常用来表明类的实
例中所有属性和操作都必须遵守的一种契约。
例如:在保龄球赛类中有 3 个方法:
1.
addPlayer() 增加一个选手、
2.
removePlayer() 删除一个选手
3.
getMaxPlayers() 求参赛选手的最大数目。
在构造一个保龄球赛对象 B 时,若不变式为
B.getMaxPlayers() = 0,则使用 B.addPlayer() 将违反该操作
的契约。因此应将不变式改设为 B.getMaxPlayers() > 0。
编写不变式的目的是为了说明类实现者为类使用者所做的约
束。因此,类实现者应注意这种约束应是简单的、明确的边
界情况。
31