2005年06月24日

基 本 标 签

  创建一个HTML文档 <html></html>
  设置文档标题以及其他不在WEB网页上显示的信息 <head></head>
  设置文档的可见部分 <body></body>

             标 题 标 签

  将文档的题目放在标题栏中 <title></title>

            文 档 整 体 属 性
  设置背景颜色,使用名字或十六进制值 <body bgcolor=?>
  设置文本文字颜色,使用名字或十六进制值 <body text=?>
  设置链接颜色,使用名字或十六进制值 <body link=?>
  设置已使用的链接的颜色,使用名字或十六进制值 <body vlink=?>
  设置正在被击中的链接的颜色,使用名字或十六进制值 <body alink=?>

             文 本 标 签

  创建预格式化文本 <pre></pre>
  创建最大的标题 <h1></h1>
  创建最小的标题 <h6></h6>
  创建黑体字 <b></b>
  创建斜体字 <i></i>
  创建打字机风格的字体 <tt></tt>
  创建一个引用,通常是斜体 <cite></cite>
  加重一个单词(通常是斜体加黑体) <em></em>
  加重一个单词(通常是斜体加黑体) <strong></strong>
  设置字体大小,从1到7 <font size=?></font>
  设置字体的颜色,使用名字或十六进制值 <font color=?></font>



              链 接

  创建一个超链接 <a href="URL"></a>
  创建一个自动发送电子邮件的链接 <a href="mailto:EMAIL">
  </a>
  创建一个位于文档内部的靶位 <a name="NAME"></a>
  创建一个指向位于文档内部靶位的链接 <a href="#NAME"></a>

            格 式 排 版

  创建一个新的段落 <p>
  将段落按左、中、右对齐 <p align=?>
  插入一个回车换行符 <br>
  从两边缩进文本 <blockquote>
  </blockquote>
  创建一个定义列表 <dl></dl>
  放在每个定义术语词之前 <dt>
  放在每个定义之前 <dd>
  创建一个标有数字的列表 <ol></ol>
  放在每个数字列表项之前,并加上一个数字 <li>
  创建一个标有圆点的列表 <ul></ul>
  放在每个圆点列表项之前,并加上一个圆点 <li>
  一个用来排版大块HTML段落的标签,也用于格式化表 <div align=?>

           图 形 元 素

  添加一个图像 <img src="name">
  排列对齐一个图像:左中右或上中下 <img src="name" align=?>
  设置围绕一个图像的边框的大小 <img src="name" border=?>
  加入一条水平线 <hr>
  设置水平线的大小(高度) <hr size=?>
  设置水平线的宽度(百分比或绝对像素点) <hr width=?>
  创建一个没有阴影的水平线 <hr noshade>


             表 格

  创建一个表格 <table></table>
  开始表格中的每一行 <tr></tr>
  开始一行中的每一个格子 <td></td>
  设置表格头:一个通常使用黑体居中文字的格子 <th></th>

            表 格 属 性

  设置围绕表格的边框的宽度 <table border=#>
  设置表格格子之间空间的大小 <table cellspacing=#>
  设置表格格子边框与其内部内容之间空间的大小 <table cellpadding=#>
  设置表格的宽度-用绝对像素值或文档总宽度的百分比 <table width=# or %>
  设置表格格子的水平对齐(左中右) <tr align=?> or <td align=?>   
  设置表格格子的垂直对齐(上中下) <tr valign=?> or <td valign=?>
  设置一个表格格子应跨占的列数(缺省为1) <td colspan=#>
  设置一个表格格子应跨占的行数(缺省为1) <td rowspan=#>
  禁止表格格子内的内容自动断行回卷 <td nowrap>

              窗 框

  放在一个窗框文档的<body>标签之前,也可以嵌在其他窗框文档中 <frameset></frameset>
  定义一个窗框内的行数,可以使用绝对像素值或高度的百分比 <frameset rows="value,value">
  定义一个窗框内的列数,可以使用绝对像素值或宽度的百分比 <frameset cols="value,value">
  定义一个窗框内的单一窗或窗区域 <frame>
  定义在不支持窗框的浏览器中显示什么提示 <noframes></noframes>

             窗 框 属 性

  规定窗框内显示什么HTML文档 <frame src="URL">
  命名窗框或区域以便别的窗框可以指向它 <frame name="name">
  定义窗框左右边缘的空白大小,必须大于等于1 <frame marginwidth=#>
  定义窗框上下边缘的空白大小,必须大于等于1 <frame marginheight=#>
  设置窗框是否有滚动栏,其值可以是"yes","no","auto",缺省时一般为"auto" <frame scrolling=VALUE>
  禁止用户调整一个窗框的大小 <frame noresize>

               表 单

  对于功能性的表单,一般需要运行一个CGI小程序,HTML仅仅是产生表单的表面样子。
  创建所有表单 <form></form>
  创建一个滚动菜单,size设置在需要滚动前可以看到的表单项数目 <select multiple name="NAME" size=?></select>
  设置每个表单项的内容 <option>
  创建一个下拉菜单 <select name="NAME"></select>
  设置每个菜单项的内容 <option>
  创建一个文本框区域,列的数目设置宽度,行的数目设置高度 <textarea name="NAME" cols=40 rows=8></textarea>
  创建一个复选框,文字在标签后面 <input type="checkbox" name="NAME">
  创建一个单选框,文字在标签后面 <input type="radio" name="NAME" value="x">
  创建一个单行文本输入区域,size设置以字符计的宽度 <input type=text name="foo" size=20>
  创建一个submit(提交)按钮 <input type="submit" value="NAME">
  创建一个使用图象的submit(提交)按钮 <input type="image" border=0 name="NAME" src="name.gif">
  创建一个reset(重置)按钮 <input type="reset">

最近做一个业务处理系统,因为它原有的用户系统使用ASP开发,在新的业务系统中使用了ASP.NET,ASP.NET APPLICATION要使用原来的ASP用户系统,于是问题出现了,ASP APPLICATION怎样才能让用户登录的状态及用户信息在ASP.NET中依然有效呢。于是我们考虑用构造FORM来自动提交传递ASP应用中的Session变量。
例子如下
ASP应用URL为http://127.0.0.1/asp/,并在ASP.NET应用中的web.config设定
  <!–设定ASP应用的URL–>
  <add key="aspURL" value="http://127.0.0.1/asp/" />

在ASP应用中增加两个ASP页面system.asp和autoPostForm.asp
<!–system.asp–>
<%
Session("UID")="user"   
session("isPass")="ok"   
Server.Transfer("autoPostForm.asp")
%>

<!–autoPostForm.asp–>
<%
Response.Write("<form name=t id=t action=""http://127.0.0.1/aspdotnet/getSession.aspx""

method=post >")
Response.Write("<input type=hidden name=UID" )
Response.Write( " value=" & Session("UID") & " >")
Response.Write("<input type=hidden name=isPass" )
Response.Write( " value=" & Session("isPass") & " >")
Response.Write("</form>")
Response.Write("<script>t.submit();</script>")
%>

在ASP.net应用中用页面getSession.aspx来接受传递过来的Session变量值

getSession.aspx.cs代码片段:
private void Page_Load(object sender, System.EventArgs e)
  {
 
  if(!Page.IsPostBack)
  {
   string aspurl=ConfigurationSettings.AppSettings["aspURL"].Trim();     
   try
   {
    string fromurl=Request.ServerVariables["HTTP_REFERER"];
   
    //验证是否从asp应用中提交过来
    if(fromurl.StartsWith(aspurl))
    {
     string uid=Request["UID"].ToString();
     string state=Request["isPass"].ToString();
   
      if(uid!="" && state=="ok")
      {
       //表明用户在asp系统中已登录成功
   
      }
 
    }
    else
    {
     Response.Write("<script>alert(‘非法用户或未登录用户’);top.location.href=’" + aspurl +

"’;</script>");
    
    }
   
   }
   catch
   {
    Response.Redirect(aspurl);
   }

  }
}

可以做成通用的,那就在autoPostForm.asp使用

For each sItem in Session.Contents
Response.Write("<input type=hidden name=" & sItem)
Response.Write( " value=" & Session.Contents(sitem) & " >")
next

而在getSession.aspx页面用下面的代码来接受并用同名Session变量保存

for(int i=0;i<Request.Form.Count;i++)
{
Session[Request.Form.GetKey(i)]=Request.Form[i].ToString();
}

2005年06月21日

C#中的委托类似于C++的函数指针,适合以下情况:

调用一个单一方法;
一个类要进行方法规范(method specification)的多种执行;
使用一个静态方法来执行规范;
想获得类似事件设计的模式;
调用者没有必要知道或获得方法定义的对象;
执行的提供者想将规范的执行“分发(hand out)”成一些可供选择的部分;
代码需要进行简单的组成部分。

参考以下链接:

http://www.donews.net/shanyou/archive/2004/10/07/122671.aspx

http://www.donews.net/shanyou/archive/2004/10/07/122712.aspx

2005年06月01日

林星 (iamlinx@21cn.com)
2001 年 11 月
来自《IBM DeveloperWorks》

在大规模的需求调研展开之前,有一个重要的工作要做。这项工作在项目中所占的时间跨度非常的小,但是却有非常重要的意义。不同的人、不同的方法对这项工作有不同的描述,在我们的文章中,根据UP的思想,称之为"业务建模"。

所有的项目都有业务建模时期

1. 业务建模是什么
业务建模(Business Modeling),业务建模是一个复杂的过程,对其下一个准确的定义是困难的事情。在RUP的词汇表中将其解释为:
"包含您可用来对业务进行可视化建模的所有建模方法。这些是您可用于执行业务工程的方法的子集。"

从定义中可以看出,它是一种建模方法的集合,目的是对业务进行建模。这方面的工作可能包括了对业务流程建模,对业务组织建模,改进业务流程,领域建模等方面。

2. 为什么要业务建模
Brooks 大师说,三十多年来各式各样的应用系统(Application Programs AP)历经多次的修修改改,已经变得面目全非,如同一群的怪兽,难以驯服。

Rogerson大师也说,The application is a rock in the river of change.(应用(系统)成为改变之潮流中的顽石)。

对很多企业而言,有一个统合企业各部门的信息系统的心愿似乎已经成了一种奢望。企业中或多或少都会有一些应用系统在辅助企业的自动化运作,当企业信息主管希望能够对目前的信息系统进行整合,能够配合企业的发展的时候,他们失望了。大多数的应用缺乏一个统一的接口,难以进行整合。

在我们进行项目开发的银行中,我们也同样发现了这个问题,不同部门的系统之间无法进行互联,跨部门的业务流程必须经过手工的处理。

以前,应用程序的开发都是基于部门的功能的而建的。单纯只是为了解决目的而建立应用系统。所以这种方式建立的应用系统是针对特定的功能区域(Function Area)而建立的。至于如何使企业内的多个应用系统共同运作,就不在设计者的考虑之列了。随着企业的发展,就会发现企业需要变化以适应市场变化,业务发展的时候,原有的一系列应用系统却成了企业发展的拦路虎,这使得企业不得不回到手工的时代。

针对这种情况,有没有相应的解决之道呢?解决的方法就是从业务建模入手,而不是从较低层次(部门级或以下)入手。通过用例分析技术,建立企业的业务模型,进行适当的切割,选取稳定的软件架构,分析出企业的业务实体(Business Entity 企业中微小不可分的事物,抽象或具体的,如帐户,契约等,又被称为Business Object),以此为基础,组装出组件(Component),落实到相应的三层结构,建立针对特定功能区域的应用系统。

以这样的流程做出来的企业应用系统,不论规模是部门级的,还是企业级的,都有扩展的余地。以组件为基础的软件三层构架,也能够较好的配合企业的业务变化而变化(相应变化的代价较小)。而整个流程的第一步,就是业务建模。

在前一段时间,中国很流行企业流程再造BPR(Business Process Re-engineering)这个名词。BPR这一名词中的R(Re-engineering)一词是由Dr. Hammer提出,说明企业必须推动四个层面的重新设计:Re-position、Re-organization、Re-system、Re-vitalizing之再造工程;名称中的P(Process),更是管理上由销售、采购到财务、生产各层次,力求降低成本、提高产出,所必须精密设计的企业管理流程或程序。这个词目前都是和ERP串联在一起,成了ERP的前置工程,更成为保证ERP能建立企业完美管理体系,以支持高绩效的最重要因素。实际上呢,这个BPR就是我们所谈到的业务建模。

可以看出,业务建模在ERP工程中被着重强调,而且ERP中的BPR已经成为一门独立的学科。不仅如此,即便是在普通的信息系统中,业务建模也是非常重要的,所不同的,仅仅是它们的规模而已。这一点上,可能大家会不理解,如果你只是为企业的业务自动化建立应用,直接照搬企业模式不就行了吗。这里有两点原因,一是企业原有的业务模式在以人为主的环境中可能运行的很好,可是把这套模式原本不动的搬到计算机上就未必会适合了。人的能力和计算机的能力有很大的出入,所以流程必须经过调整以适应计算机;第二个原因是上面已经提到过的避免产生部门级的,部分功能区域的应用系统。

在RUP中,业务建模被作为下游流程的输入重点强调:业务模型是需求工作流程的一种重要输入,用来了解对系统的需求。业务实体是分析设计工作流程的一种输入,用来确定设计模型中的实体类。(RUP)

3. 需求和业务建模
业务建模是需求工程中最初始的阶段,也是整个项目的初始阶段。需要指出的是,业务建模时间的跨度在不同的项目中有很大的差别的。在有些项目中,例如大型ERP系统,可能需要几个月的时间。而对于普通的项目,业务建模的时间可能仅仅需要几天的时间。

需求是技术无关(technology independent)的。在需求阶段讨论技术是没有任何意义的。那只会让你的注意力分散。技术的实现细节是在后面的分析、设计阶段才需要考虑的事情。而在业务建模阶段,不但要保证需求的技术无关性,还要保证你的需求不要深入细节。因为在业务建模阶段,最重要的事情就是要了解业务的全貌,深入细节会浪费时间和精力。要知道,讨论一个企业里的业务细节,就算给你一个月的时间也没必能够结束。

在实际中,这两点都是很难做到的。例如企业原先有一个系统,这就不得不由你讨论和新旧系统的兼容问题。这时候就要注意,如果你是讨论就有系统的架构的话,那还是属于技术无关的范畴,如果你一旦进而讨论各具体模块/组件的细节,那就非但不是技术无关,而且也深入细节了。在不深入细节的问题上,往往你很难禁止项目涉众(Stakeholder)①不去讨论一些相关的业务细节。这个时候你可以将这些细节记录下来,然后再回到业务建模上。

①A stakeholder is defined as anyone who is materially affected by the outcome of the project.
涉众是所有会受到项目结果重大影响的人。

4. 业务建模时期的主要任务
项目涉众的共同愿景:要想项目成功,可离不开项目涉众的支持。在项目一开始,不论是项目涉众还是开发人员,对项目的任务、范围都是模糊不清的。但随着项目的深入,原来模糊的景象会慢慢清晰、立体起来。但是为了不浪费时间,我们有必要在项目射入之前,现在项目涉众中竖立一个共同的愿景。

共同愿景的竖立可没有想象中的那么简单,因为每位涉众都关心自己的利益,都有自己的评判标准。你可以把大家的意见都列在白板上,然后逐项集中讨论,做出修正,直到大家的意见一致的时候为止。在共同远景的竖立过程中,其实有两件事情也已经同时做了,项目范围(scope)和高阶(high-level)需求。

项目范围:项目该做什么,不该做什么,需要在一开始就有明确的定义。对于项目范围内的需求,一个也不要放过,而项目之外的,一个也不要去关心。虽然有的时候,范围的变化会有利于项目本身,例如客户的合理要求或是市场目标客户的变化,但是这种变化应该要在"资源能够支持"和"得到审批"的前提下进行。

项目范围的描述可以通过陈述和图示来进行。我建议大家使用图示。因为陈述语句比较含糊不清。例如常常听到有客户说。"我要建立我公司的电子商务系统。"这句话就是含糊不清的,你的电子商务系统是销售什么产品?面向什么客户?是否要支持在线支付?根据这些疑问,这个陈述句可以做进一步的修改,"建立在线订货系统,针对当前的目标客户销售公司的目前产品。"这样就清楚许多了。不过图示的方法会更好一些,在图的选择上,你可以使用DFD图或是用例图。根据经验,DFD图比较容易为客户所接受。

高阶需求:这个部分我们在下面会详细讨论。既然是高阶需求,就不能讨论过多的细节。在讨论高阶需求的时候,尽量保证快速的讨论出系统的概貌,建立需求模型,得到项目涉众的一致通过。

取得支持:为了保证需求计划的顺利进行,取得项目涉众的支持至关重要。你可以选择在这个时候告诉项目涉众他们的权利和义务,以及开发人员的权利和义务。在这个方面,具体的我不想多说,大家可以参考『软件需求』的第二章。主要的就是"涉众有改变需求的权利,同时要承担向开发人员讲解需求的义务。"开发人员的权利和义务正好和涉众相反。

业务建模会议:所有的这一切都通过业务建模会议进行,和其它会议不同的是,这个会议需要所有的项目涉众参加,如果不能获取所有项目涉众的意见,那就不是完美的。会议中最重要的工具就是白板,一位出色的速记员也是必须的。

建模原理

5. 业务建模中的用例
在上一篇中我们讨论了很多用例的知识,可是落实到企业中的时候,我们往往会感觉难以把握企业的用例,这一点我们在用例的误区中也有提到。在实际的情况中,你可能会对角色的归类,用例的划分,粒度的把握等很细节的方面都没有底,偏偏这些实际的东西对你的项目有非常大的影响。

RUP中,有多种的概念来支持用例的实现:业务主角(Business Actor)、业务实体(Business Entity)、业务用例(Business Use Case)、业务角色(Business Worker)、业务用例实例(Business Use-Case Instance)。为了能够比较清楚的展示出业务建模,我们采用了UP方法的代表――RUP。但在实际中,要视大家具体的情况而定,这里所讲到的概念,都是为了帮助大家理解建模过程,并不是让大家生搬硬套。

在我们对系统还丝毫不了解的时候,我们就会把系统看成一个很大很大的黑盒,这个大黑盒子我们会叫他业务域(Business Domain),把它的外部看成一个业务环境(business environment)。而那些在业务环境中和业务域有关系的人(也可能是物)就被称为业务主角(Business Actor)。在实际的例子中,我们可能会把信贷业务(注意不是信贷业务系统,这里是业务建模,系统还不存在。)称为业务域,我们经过调查,发现平时和信贷业务打交道的有客户,人民银行,外汇管理局,其他银行,信贷部门使用的其他系统,银行内部的其他部门。所以这些人(物)就是业务主角。业务主角的实例一般包括了客户、供应商、合作伙伴、潜在客户("市场")、当地政府、在业务中未建模部分工作的同事等。必须注意的是,业务主角表示的特定类型的用户,而不是某一个具体的用户。一个角色可能会有很多实际用户担任,一个实际的用户也可能会担任很多的角色。

业务用例以及业务用例实例在RUP中的定义如下:
A business use-case instance is a sequence of actions performed in a business that produces a result of observable value to an individual actor of the business. A business use case defines a set of business use-case instances. A business use case has a name.

业务用例实例是在业务中执行的一系列动作,这些动作为业务的个体主角产生具有可见价值的结果。业务用例定义了一组业务用例实例。业务用例具有名称。

刚开始大家可能会对业务用例以及业务用例实例有所疑问。其实可以把他们看成基类和子类的关系。在一个企业中,具体的工作流程可能有很多很多,比如你去麦当劳的时候,点汉堡和点薯条的工作流程就不一样。众多的流程给需求的调查工作造成了一定的难度。即使是最古老的哲学也告诉我们,表面现象是复杂的,本质是简单的。为了简化需求工作,我们就把点汉堡和点薯条归纳为点餐。这样,点餐就是一个业务用例,而点汉堡、点薯条就是相对应的业务用例实例。

业务用例
业务用例

 

业务角色和业务主角的概念也很容易让人摸不着头脑。其实看它们的英文愿意会更容易理解它们的区别:Business Worker,Business Actor。Worker有工人的意思,而Actor有参与者的意思。所以它们的区别就是一个在内部,一个在外部。业务角色是实现业务用例的人,业务主角是和业务有关的人。例如,对银行的押汇业务而言,客户就是业务主角,他和业务有关。而押汇人员就是业务角色,因为他们是实现业务用例的人。在RUP中,二者定义如下:

A business worker represents a role or set of roles in the business. A business worker interacts with other workers and manipulates business entities while participating in business use-case realizations.

业务角色代表业务中的一个或一组角色。参与业务用例实现时,一个业务角色和其他角色进行交互,并操纵业务实体

A business actor represents a role played in relation to the business by someone or something in the business environment.

业务主角代表了与业务有关的角色,此角色由业务环境中的某个人或物来担任。

业务角色
业务角色
业务主角
业务主角

 

分辨业务角色和业务主角要看环境而定。当你开发企业的ERP系统时,部门的员工都属于业务角色,而你开发一个部门级的应用时,其他部门的员工可能属于业务主角。

业务实体,在一些文章中被称为商业对象(Business Object)。不论怎么叫,所表示的意义都是一样的。例如在银行信贷这个例子中,我们就涉及到很多业务实体:契约、单笔贷款、客户等。所以业务实体就是企业中那些很基本的要素。如果觉得银行押汇的例子不好理解。可以想象餐厅中的菜单、汉堡等都是业务实体。在RUP中,业务实体被定义为:
A business entity represents a "thing" handled or used by business workers.

业务实体代表业务角色处理或使用的"事物"。


业务实体

 

在很早以前,我们讨论过需求易变性。相对于需求的不断变化,可是业务实体对象在一段相当长的时间内都存在。航空公司今天打折,明天又不打,还有明折、暗折。可是机票从来没见有什么大的变化,从来也只有那几样属性:价格、航班、出发地、目的地。所以业务实体是比较稳定的。这对于我们是有很大的意义的:

"一个业务实体经常代表某个对多个业务用例或用例实例有价值的事物,因此,业务实体对象的生存期相当长。一般而言,一个好的业务实体不包含关于其使用主体和使用方法的信息。"(RUP)

由业务实体组成的业务用例会稳定很多。在以前,开发方式采用模块为基础的方法,需求变化的时候,只好改写模块。如果采用稳定的业务实体来实现业务用例的话,业务用例的改变只需要对业务实体进行重新的组合。当然,这里还需要很多的技术来实现,并没有那么简单。要知道,四个现代化可不是一天就能够实现的。

还有一个使用业务实体的重要原因:业务实体的特性决定它具有天生的重用性。就像麦当劳的销售系统中有汉堡实体,生产系统中也有,供应链系统中也有。天哪,这世界真是美好!

使用业务实体一个很大的困惑是应该把它做为类还是属性。这个取决于业务环境对这个实体的重视程度。一个客户在银行信贷部门是一个很重要的类,而在押汇部门就只是信用证实例的一个属性。这个问题非常的重要。设计时的失误可能会导致今后系统改进的极大痛苦。例如本该设计为类的业务实体设计成了属性,在今后增加属性的时候不得不面对着数据库的调整和系统的修改。

6. 建立业务用例模型
业务用例模型(business use-case model),在RUP中定义为:
The business use-case model is a model of the business intended functions. The business use-case model is used as an essential input to identify roles and deliverables in the organization.

业务用例模型是说明业务预期功能的模型。作为一个核心输入模型,业务用例模型用于确定组织的各个角色和可交付工件。

从业务用例模型的定义可以看出,它是企业最核心,最概括的业务说明。它主要是由业务用例和业务主角构成的,其主要目的是说明客户和合作伙伴是如何开展业务的,它描述业务的主要方式是通过业务用例的方式。下图为RUP中业务用例模型的图示。

业务用例模型
业务用例模型

 

从图中我们也可以很清楚的看出业务用例模型包括一组的业务用例。这是因为企业中的业务通常都会由多个的业务用例的多个实例构成。这些业务用例形成的企业工作流程可能会由业务主角所引发,也可能会由业务规则②所引发。

②业务规则(Business Rules):业务规则是必须遵守的政策或条件的声明。(Business Rules are declarations of policy or conditions that must be satisfied.)

业务用例模型实际上就是企业经营业务的一种描述,为了建立完整、准确的企业用例模型,应该将注意力专注于企业的业务做了些什么事情,而不应该集中于如何做。虽然这样可能会产生一些业务用例相冲突,相重复的情况,但是RUP的思想在于迭代,这项工作完全可以在接下去的迭代周期内完善。

业务用例模型是和企业业务最贴近的计算机模型。它的很多思想和企业日常经营如出一辙。在企业的日常活动中,业务的种类可能有很多种。在一些讲述ERP思想的文章中,通常会强调三类:

一种是和主营业务密切相关的工作,例如银行的营业部、信贷部、押汇部等。这种工作通过人的劳动,将一种资源转变为另一种资源,产生价值。

一种是管理型的工作,例如公司的管理层,财务部门等。这种工作本身并不产生价值,但是它通过指导、管理、检测第一种工作,加大第一种工作的产出价值。

还有一种称为支持工作,例如系统管理、安全等。它并不是很重要,具有支持其他工作的性质。

业务模型同样可以使用这种分类。通过这种分类,可以更好的把握核心业务用例,为下一步的工作打好基础。

有很多业务用例是由业务主角触发的,RUP中也把和业务主角有关联关系的业务用例称为核心业务用例(Core Business Use Case)。这强调了构建业务模型的目的是为了提供以用户为中心的服务。这也是我们建立业务用例的时候应该注意的。

当然,有时候业务用例的触发是为了产生用户需要的结果。例如企业的市场调查行为就不是由业务主角触发,而是企业积累了大量用户请求的结果。而对于管理型、支持型的,不直接和业务主角的客户类发生联系,但是也有其特定的业务主角,如管理型的业务用例需要和董事会为发生联系,支持型的业务用例可能和供应商发生联系。

在建立了基本的业务用例模型之后,对此模型进行精化是非常有必要的,这时候,在上一章中我们介绍的用例的扩展关系和使用关系就有了用武之地。除了这两种关系,还有一种新的关系。

7. 在业务建模中使用关系
泛化关系(Generalization):根据我的理解,可以把它看作我们比较熟悉的继承关系很相似的一种关系。Generalization一词含有一般化、概括的意思。它是一个相对抽象的词。虽然它和继承关系非常相似,但是它们在使用环境和产生目的方面都有相异之处。下图描述了四个业务实体之间的泛化关系:

 

当你去麦当劳的时候(不要误会,我并不是很经常去的),会选择麦香鸡汉堡、麦香鱼汉堡或是吉士汉堡。但是分别对这三种汉堡建立业务实体就非常没有意义。所以可以将它们概括为汉堡这个业务实体。同样的道理,企业的业务流程中也可以概括出一些共有的属性和行为。为了避免多次说明同一个工作流程,您可以将共有的行为放在一个单独的业务用例中。称为父用例,执行子用例的用例实例将遵循父用例的事件流,同时插入附加行为或修改在子用例事件流中定义的行为。

8. 方法的选择
以上的原理我采用了UP的方法。但是除了UP方法,还有XP、FDD等方法。所以在做业务建模的时候,也要根据不同的方法选择适当的工件。例如素材和功能。方法的好坏并不是我们这片文章讨论的重点,我会在另一篇文章中讨论方法。再一次需要强调的是,上面讨论的RUP的工件只是为了学习,所以才定义了比较复杂的工件,区分了它们之间的区别。但是在实际中,并不需要这么多的工件,那只会使你的项目涉众和开发人员糊涂。这些工件的区别只要在你心中就可以了。至于具体的实践,我们会在下一篇文章中讨论。

原则(Principle)

1. 谁才是"上帝"
<我们说客户是上帝,是因为客户的重要性,客户占有决定性的地位。可是在软件开发中,到底是谁有决定权呢?是出资方,还是项目经历,或是程序员?

职责不清,是业务建模失败的主要原因之一。我们可以很容易的看见客户把自己的要求用几句话高度浓缩之后扔给开发人员,或是开发人员按照自己的意思开发程序。难道说,客户和开发人员之间真的是"心有灵犀",完全不用沟通的吗。

我在前文中已经提到过项目涉众和开发人员的权利和义务,我想这里还有必要再次强调。Scott W. Ambler说:

it is the role of project stakeholders to provide requirements, it is the role of developers to understand and implement them.

在我一次开发过程中,遇到过一个很有意思的涉众,他在他的需求分析中这样写:"总之,要实现那种能够想到就能做到功能。"我想,这位涉众对我们的计算机工业有着非常远大的抱负。但我不得不客气的告诉他目前实现是不太现实的。

大家听了可能会觉得好笑,但是在我自己和客户打交道的生涯中,这种客户不在少数。现实就是这样,你要怎么做?是一笑之后,弃之不顾吗?我想大多数人会这样做。因为他的要求太荒唐了嘛。可是,你有没有花时间去了解一下,他这句荒唐的话背后代表了他的什么意思呢?我觉得,软件开发人员负有教育涉众的义务,你需要引导你的涉众,让他们说出自己的心声。我在看到这位涉众的这句话后,就花了一些时间去了解,其实呢,事情很简单,他就是想要一个能够定制报表模板的功能。而在项目结束后,我和这位涉众有幸再一次的合作,这时候,他已经成为了一个出色的客户方的项目领导者了。

这个例子说明了什么呢?涉众往往都是领域专家,对自己的工作有很深的认识,可是由于对软件开发的不了解,涉众往往表达不清,甚至表达不出自己的需求。这时候,就是体现你的功力的时候了。记住,象对待上帝一样对待你的客户。

2. 耐心是首要的
明白了谁是"上帝",那就要耐心的对待上帝。学理工科的人,一般在逻辑思维上会比较好,可是对于涉众来说,可不一定是这样。我就遇到过一位做档案工作的涉众,在了解需求的时候,扯东扯西,含糊不清。明明一分钟前才否定的方法,下一分钟又提出来了。我觉得我的脾气应该是不错的,可到最后也几乎发飚。所幸,最后我终于撑了下来。我想,在经历过这件事情之后,我的耐心指数又会有一个很大的提高了吧。

我有一位同事的耐性是我所佩服的,在一个网站项目中,他负责系统分析。他整整花了三天的时间,和网站项目的负责人住在一起。最后系统分析出来之后,他的精神还不错,可是那位负责人已经快不行了。

当然,这只是个笑话,并不是去鼓励大家拼命。劳逸结合还是很重要的,身体是革命的本钱嘛。但是这告诉我们,如果缺少耐心,需求是很难成功的。例如在业务建模的讨论会上,你虽然规定了这次的会议讨论的是高阶需求,可是与会者总会时不时的争辩一些芝麻绿豆的小事儿。你怎么办?我相信这种情况是很普遍的。

耐心最后会仍会体现为沟通,只有耐心的沟通,你才能揭开需求的重重面纱。人的行为总是会受到思想的指导,如果你解不开涉众的心结,你就不可能了解他真正需要的。

我的一位项目涉众的表现很奇怪,她总是在不停的说一件事情:"她要实现报表自动生成。"她的需求讲来讲去好像总脱不出这个范围,可是我从她那里几乎听不到别的东西了。这时候,我决定放弃了,我想从她哪里可能已经没有更多的资料了。但在我了解到她的工作中报表处理占用了她大量的时间之后,我改变了想法。在我花了一些时间帮她理清了报表处理的思路之后,她还在其他方面给了我很大的帮助。

3. 参与是重要的
XP方法的一个重要实践,就是提倡"现场客户"(on-site customer)。也就是说,客户应该随时和开发人员在一起,随时提供资料和做出决策。而这个客户,也必须领域专家,而且能够有权做出决策。

这种现场客户相信国内的软件组织多半还做不到。但是一定要往这方面努力。我认为,这种现场客户有两种人:一种是项目涉众,还有一种是行业专家。其实很多软件公司都会配备一些管理咨询人员,这些人就是行业专家。有数据统计说,目前广东省软件公司中的咨询人员和开发人员的比例达到了3:1。我觉得这是好事。项目涉众往往对自己的工作中的事务性部分有很深的认识,但是很难将之提升到一个理论的水平。这时候就需要一些行业专家来帮助了。让行业专家和项目涉众共同探讨,还能够激发项目涉众的灵感,想到原来他想不到的方面。这就是"潜在需求"的开发。

另一方面,参与还意味着需要项目涉众全身心的投入到业务建模的过程中来,要能够调动他们的积极性。因此呢,太复杂的流程会阻碍涉众的参与。所以,使用一些简单的、能够为客户所接受的工件(Artifact)来进行业务建模是很有必要的。我说过前面讨论的那些"主角"啊,"用例"啊,那是理论,是给开发人员看的,让开发人员心里有个底。你给涉众看这些,他们能懂吗?等他们了解了这套机制,恐怕黄花菜都凉了吧。

素材(User Story)、特性(Feature)、CRC卡片这些都是很不错的工件,既简单,又能够满足需要。

知识点:素材和特性都表述了用户的一个简单的要求,它能够在较短的时间内完成。素材是XP方法中的工件,特性是FDD方法中的工件。CRC是class、 responsibility、collaborator的缩写,它是一张分为三个部分的卡片,分别标记了类名,类的责任,以及类之间的合作关系。非常的贴近客户,甚至可以在做游戏的过程中完成卡片的填写,能带来很强的客户参与度。

4. 拥抱变化
我想这一点会遭到开发人员点一致指责。毕竟,需求变化是开发人员最讨厌的一件事了。不错,我也讨厌。可是,就像我们常说"哭不能解决问题"一样,讨厌能解决问题吗?拒绝客户的变更要求,要求客户在需求规格说明书上签字。这些做法只能是适得其反。没有任何正面的、积极的意义。

必要的需求变更管理是重要的。因为无休止、无控制的变化必然会造成资源的极大浪费。但从另一方面说,需求变更被接受的评判标准应该是"是否合理",而不是"是否易于实现"。

需求变更要求我们的开发工作要迭代式进行,包括需求、设计、实现等阶段。这样才能将变更风险减到最小。这一点我们在讨论具体需求建模的时候会进一步讨论。

拥抱变化的更高一个层次是提前预估变化。制定一个可能的变化清单来记录可能出现的变化。最简单的例子就是一个企业在开发了进销存系统之后又希望能够开发财会系统与之相连。如果你能够预先留下伏笔,相信能够省不少力气。预估变化的另一种做法是通过使用模式。但是切记,模式的使用也不能过头。这些是题外话,如果有机会我会在其他的文章中集中讨论这方面的问题。

实践(Practice)

5. 建模会议
会议是业务建模最重要的手段。尽管会议在中国总是背负着一些骂名,但是只要组织得好,它是一种相当有效的沟通(Communication)手段。建模会议是一种大范围的会议,换句话说,所有的相关人员都应该参加会议。因为在业务建模时期,主要的目的就是建立对系统的高阶需求,这就要求众多项目涉众的共同参与,以保证需求的广泛性。所以呢,建模会议的规模是相当大的。出资方、高级经理、经理、直接用户、开发人员,各方各面的人都应该参加或是派代表参加建模会议。

如果大家有过参加大型会议的经验都知道,越是大型的会议,它的决策效率就越低。这是正常的。因为一个人的时候,不需要沟通,决策效率最高。等到两个人的时候,他们需要沟通的时间来进行决策。等到三个人的时候,这个沟通并达成一致的时间就更长。如果人数到了四个人、五个人甚至一二十个人,那么大部分的时间都会花在沟通上。更何况,人和人之间还有观念不同、利益之争。所以呢,为了保证会议的效率和效果,应该遵循一定的规则:

做好准备:如果你要开会,与会者连内容都不清楚,那你会怎么办。你必须首先花很多的时间来说明你开会的目的,是不是。要事先将会议的主题、议程连同会议通知发送给与会者,让他们先有个准备,会议开始时就能够迅速进入正题。

尽量邀请最多的人:我已经说过,如果建模会议不能够听取最广泛的意见,它就不是一个成功的会议。可是在现实中,这点往往难以做到。因为目标组织做为客户,往往都很"拽",在没有充分认识会议的重要性之前,要做到全部到场简直就是不可能。而客观上也会有出差、休假、有要事等原因做不到这一点。这里,一方面你需要向目标组织的决策者阐明利害,让他们重视。另一方面,你也需要积极主动的邀请项目涉众参与。因为邀请所有的人是不可能的,所以就尽可能的多吧。

分出与会者的级别:我很喜欢那种有一个内圈、一个外圈的会议室。因为我邀请所有人是件无法做到的事情,所以我首先要保证核心人员能够全部到场,坐在内圈,然后才是次要的人员,坐在外圈。核心人员是和你的项目息息相关的那种人。比如,财务系统,财务主管就是核心人员。要保证核心人员全员到场,至于次要人员,越全越好。

从底层开始:中国人有个比较不好的习惯,就是老板说一的时候,他决不会说二。所以要先让底层先说话,然后才轮到中层,再到上层。开发人员是不说话的,他们要么听,要么引导大家说话。如果我们一开始就先让领导来训话一番,那底层的人也就不用再说什么了。

列举所有涉众的所有观点:首先要让大家能够对新的系统畅所欲言,然后把所有的观点都罗列在白板上。这里头可能会有一些观点会是非常荒唐的,但没有关系,尽管写上去。这是一个脑力激荡的过程,很容易产生出新的idea。主持的开发人员的主要工作就是引导和鼓励大家说出更多的想法,并记录下来。这里我们稍微离题一下,有人说中国人在会议上大都不愿意发表意见,我看在这种建模会议上不必过于担心此事。为什么呢,因为项目涉众不需要为他们的发言担任何的责任,说了,白说,白说谁不说。

将观点分类:想必你的项目涉众已经有些累了,创意也差不多了,你的白板估计也满了。但是你看看白板上的观点,有很多是重复的,有很多是类似的。所以你需要用逻辑的观念将这些观点归类整理。这个工作也可以由你引导大家去做。

确定优先级:对观点排出优先级也是非常重要的,它能够帮助你识别出重大的风险,并为你在制定迭代计划时提供指导。同样,这项工作也应该由项目涉众来确定。

调查主要业务逻辑:什么叫主要业务逻辑?包括了企业的主要业务流程、主要的业务规则、重大的算法。这些都是需要在一开始就十分明确的资料,需要在建模会议上了解清楚。当然,你不能对你的项目涉众说,"这个,接下来,大家谈一谈主要的业务逻辑吧。"下面的涉众一定摸不着头脑。你还是应该引导涉众,从涉众的话语中捕捉你需要的信息。

注意会议时间:人不是机器,是会累的。所以控制好会议的长度很关键。一般,这种会议会有四五个小时,根据统计,两个小时内的会议不会让人产生疲劳感。所以应该把会议分成几小段。另外,你还可以根据会议的进展来决定每小段会议参与的人数。因为,会议越往后,一些与会者就不太重要了。

避免细节:建模会议主要的目标是建立高阶需求。如果把过多的时间花在讨论鸡毛蒜皮的小事,那就会浪费大家的时间。而在此时调查需求细节是没有很大的意义的。因为你对很多的事情都还不了解,需要进一步的深入。这时候的细节对你并没有太多的帮助。

回避技术:我在一次建模会议的时候,遇到一位负责技术的涉众,他总是不停的询问系统的技术架构,推销他的设计理念。使得我不得不好几次重申,"技术问题我们会单独找时间谈。"我想,那位技术人员应该是有比较好的理念,也很希望能够表现一把,这点无可厚非。但是这个时候还不是讨论技术的时候,需求尚未明确,讨论技术实现不是本末倒置么?

做好记录:俗话说,好记性不如烂笔头。所以在会议上做好记录是非常关键的。因为这种会议的代价相当高昂,你的项目涉众不可能每天不干活,陪你开会的,就算他们肯,他们的老板也不肯。所以要充分利用好会议的成果,所以一个优秀的速记员绝对是必要的。另外,根据研究显示,如果使用录音机的话,会使得与会者心存芥蒂而不愿开口,所以,不要使用录音机。

6. 测试
在需求的初始阶段提到测试可能会觉得有些奇怪。凡是项目,总会有一个标准来考核是否成功。这里的测试指的是考核软件项目是否成功的一个"执行性目标"。

这个目标将会是软件开发最终要满足的条件,软件成功与否的判定标准。很多企业在信息化建设的时候没有一个比较明确的目标。所以在被问道这方面的问题时,他的答案往往是我的目标是建成企业的ERP系统,建设企业的信息化平台等空洞的话。这样的软件怎么开发?连结束的标准都没有。是费用用完结束呢,还是决策者说停就停了呢。目标应该有一个可以量化的标准。例如,开发物流系统的目的是为了缩短产品周转周期,降低库存;开发供应链系统是为了加强和供应商的联系,降低库存。这些和具体业务有关的指标都是可以通过细化,用多种分指标来度量的,所以是可以做到的。

我们把这种目标称为测试就是要提醒开发人员,要把满足这种目标当作最终的测试。你的软件做得再好,不是涉众想要的,又有什么用?这是很浅显的道理,可是在实际中,涉众方和开发方往往因为一些具体的因素看不到这一点。其实,这个目标在上一篇中我们也讲过,那时我们是把它叫做愿景、范围。在本质上是一样的。

这种"可执行性目标"可以使用一些因素来衡量:

7. 业务实体
业务实体(business entity)是企业中的一些起到关键作用的类别。客户、供应商、员工、订单、凭证,这种业务实体的例子可以举出很多很多。业务实体往往会成为一个很关键的因素,因为在系统中,角色操作业务实体的行为往往会分配给业务实体,例如"根据订单计算价格"会成为订单的一个行为。这样,工作流程的实现往往是多个业务实体相互合作完成的。所以业务实体设计的好坏会对系统有很大的影响作用。

业务实体设计的主要工作包括找出业务实体,确定业务实体的属性和行为。

要确定业务实体,首先必须确定角色,并从角色的行为找出业务实体。角色需要我们对目标组织进行讨论。以我个人的经验,寻找业务角色一般比较简单,但是要记住,一个人可能担任好几个的业务角色,这是经常发生的情况。从业务角色的行为,我们可以找出业务角色所处理的事物,这些就是我们所需要的业务实体。业务实体是一个单独的业务实体还是业务实体的一个属性是值得研究的。一个本该是属性的事物被判断成业务实体只是会带来一些开销,可是一个本该单独列出的业务实体却只是被判定为其它业务实体的一个属性就有可能会带来灾难性的后果,最大的可能是系统难以扩展。

在一个人力资源管理系统中,员工类可能是非常重要的一个业务实体,它可能有非常多的属性。而在其它的系统中,例如进销存,员工类只是起到一个记录、权限管理的作用罢了。再比如,在一些企业内部的自动化处理系统中,客户可能只是其它一些实体的属性,而以客户为中心的设计大行其道的现在,这种设计就有它的致命缺陷。

要确定业务实体的属性和行为,主要是要确定每个类(业务实体)要做的事情,属性则是为了能够更好的描述类和类要做的事情。利用CRC卡片是一个不错的办法。

CRC是"类"(class),"责任"(responsibility)及"辅助者"(collaborator)三者的简称,这些资料常呈现在一张卡片上。

类名称
责任1 责任1的辅助者1
责任2 责任2的辅助者2

 

通过制作这样的CRC卡片,可以比较容易的找出每个业务实体的行为(责任)和属性(辅助者)。您可能会问,为什么不直接找出属性和行为,而要多此一举呢。这个问题是我们一直在强调的。在建模阶段,我们面对的是可能对计算机技术完全不懂的涉众,所以,采用大家易于接受的方法,可以够保证需求的完整和正确。

8. 准备计划
目前在软件开发中,关于计划有两个极端的误解。

在有些软件组织中,一般不做计划,或是做一些笼统的、没什么用处的计划。一些开发人员认为,"做计划是虚的,还不如做些实际的事。"对于项目经理,或是对这种情况没有办法,或是发布的计划开发人员阳奉阴违,让计划成为一纸空文。项目执行中随意性极大,偏离方向的事情时有发生。

而在另外一些组织中,计划被视为重中之重,需要花费大量的时间、人力,做出来的计划可谓事无巨细,算无遗策。而写的出这种计划的项目经理也被视为高级人才。开发人员叹口气说,"写程序的不如写文档的。"可是在执行的时候,原来精密的计划往往漏洞百出,项目的进度一拖再拖。

我们所有人都知道那句明言:在软件开发中,要花90%的时间完成90%的项目,然后再用90%的时间完成剩下的10%的项目。为什么呢?计划不科学。

在管理学中,计划,也有叫规划的,定义是"为组织确定目标、实现目标的战略与手段、步骤、程序的过程。"打个比方说,我想要把一个箱子推倒一个地方,这个地方就是我的目的,我为了到达那里,我是不是要估计一下按什么路线推,要推多快。然后我就开始推,还要不时的和原先的计划比较比较,需不需要调整路线和速度。这个估计就是计划。

计划的目标不是消除错误,而是让所有错误变成一堆经过细心规划后的小错误。研究四种设计方式后,最终放弃三种,最多不过是三个小错误而已,但因没有做好设计而将程序重写三遍,却可能造成三个大错误。

然而为什么会出现上面提到的两个极端呢?第一种情况其实是软件行业最早期的一种形态,没有计划,资源分配混乱,软件的开发过程处于混沌、无序、自发的状态。项目的成功全凭运气和项目成员的个人能力。而第二种情况其实一个前进了的形态,最典型的代表就是我们之前提到过的瀑布模型。那这种考虑周密的计划为什么也容易失败呢?很简单,你认为你考虑周密,可是实际上却不一定。我就见过标榜自己考虑周详的计划中排出的时间表是一周7天的。看来他一开始就没打算让开发人员休息了。计划是对未来的一种估计,哪一个人能够准确的说出6个月后的情况,恐怕没人能行吧。9月11号之前,有几个人能料到那天会发生这么大的事情?那你凭什么推算出半年,甚至一年后的事情呢?另外,你是不是真的非常了解你的开发人员,以至于有信心代替他们制定计划呢?

有人说,计划没有变化快。这句话说得很对,它提醒我们,没有计划是不行的,不具备可执行性的计划也是不行的。计划不是拿来炫耀的,是要用来执行的。我们定计划的时候,可以没有华丽的词藻,美好的构想。但是我们不能没有一些要素:

  • 什么(WHAT):按顺序列出达到目标所需完成的工作;
  • 何时(WHEN):完成工作所需要的时间;
  • 做到的程度(HOW-WELL):要完成的工作以何标准来度量;
  • 资源(RESOURCES):完成工作需要的人员/资金等;
  • 谁(WHO):由谁负责完成任务。

但是我们仍然逃不开现实和计划的背离的问题。我们虽然对预计一年后的事情把握不大,对把握开发人员在想什么把握也不大。但是如果你自己想想自己两个星期后的事情应该还是能够猜的八九不离十吧。这就引出了迭代的概念。一个项目由几个甚至几十个的迭代周期组成,每个迭代周期都是比较容易估算并制定计划的。这就是迭代的思想,也是软件工程技术的一个大飞跃。说到这里,我又要吊大家的胃口了。关于具体制定迭代计划的讨论,我们会留到下一章节讨论细节需求建模的时候再谈。

9. 培训
我很难想象一个项目没有培训该如何进行。兵书有云,"三军未动,粮草先行。"我们可以理解为事先做好充分的准备。项目也是一样,在一开始就要指定好培训的计划,留出培训的时间。我想,除非是一个非常完美的团队,否则他的成员一定也还是有不懂的东西吧,如果没有培训计划,把学习的任务推倒个人头上,项目的风险就会变得难以控制。

说起培训,大家可能就会认为是大家正儿八经的坐在那里,听一位老师上课。并不是这样的,这里说的培训是一个广义范围的培训,达到一组课程、一次会议,小到一次讨论、一次交流,都可以是培训。而其目的,就是为了让团队成员拥有足够的知识和技能,来完成项目。

出处:IBM
作者:傅纯一
 

1. 什么是用例?

   在介始用例方法之前,我们首先来看一下传统的需求表述方式-"软件需求规约"(Software Requirement Specification)。传统的软件需求规约基本上采用的是功能分解的方式来描述系统功能,在这种表述方式中,系统功能被分解到各个系统功能模块中,我们通过描述细分的系统模块的功能来达到描述整个系统功能的目的。一个典型的软件需求规约可能具有以下形式: (图略)

  采用这种方法来描述系统需求,非常容易混淆需求和设计的界限,这样的表述实际上已经包含了部分的设计在内。由此常常导致这样的迷惑:系统需求应该详细到何种程度?一个极端就是需求可以详细到概要设计,因为这样的需求表述既包含了外部需求也包含了内部设计。在有些公司的开发流程中,这种需求被称为"内部需求",而对应于用户的原始要求则被称之为"外部需求"。

  功能分解方法的另一个缺点是这种方法分割了各项系统功能的应用环境,从各项功能项入手,你很难了解到这些功能项是如何相互关联来实现一个完成的系统服务的。所以在传统的SRS文档中,我们往往需要另外一些章节来描述系统的整体结构及各部分之间的相互关联,这些内容使得SRS需求更象是一个设计文档。

1.1 参与者和用例

   从用户的角度来看,他们并不想了解系统的内部结构和设计,他们所关心的是系统所能提供的服务,也就是被开发出来的系统将是如何被使用的,这就用例方法的基本思想。用例模型主要由以下模型元素构成:

  • 参与者(Actor)
    参与者是指存在于被定义系统外部并与该系统发生交互的人或其他系统,他们代表的是系统的使用者或使用环境。
  • 用例(Use Case)
    用例用于表示系统所提供的服务,它定义了系统是如何被参与者所使用的,它描述的是参与者为了使用系统所提供的某一完整功能而与系统之间发生的一段对话。
  • 通讯关联(Communication Association)
    通讯关联用于表示参与者和用例之间的对应关系,它表示参与者使用了系统中的哪些服务(用例),或者说系统所提供的服务(用例)是被哪些参与者所使用的。

  这大三种模型元素在UML中的表述如下图所示。


 

  以银行自动提款机(ATM)为例,它的主要功能可以由下面的用例图来表示。ATM的主要使用者是银行客户,客户主要使用自动提款机来进行银行帐户的查询、提款和转帐交易。


  通讯关联表示的是参与者和用例之间的关系,箭头表示在这一关系中哪一方是对话的主动发起者,箭头所指方是对话的被动接受者;如果你不想强调对话中的主动与被动关系,可以使用不带箭头的关联实线。在参与者和用例之间的信息流不是由通讯关联来表示的,该信息流是缺省存在的(用例本身描述的就是参与者和系统之间的对话),并且信息流向是双向的,它与通讯关联箭头所指的方向亳无关系。

1.2 用例的内容

   用例图使我们对系统的功能有了一个整体的认知,我们可以知道有哪些参与者会与系统发生交互,每一个参与者需要系统为它提供什么样的服务。用例描述的是参与者与系统之间的对话,但是这个对话的细节并没有在用例图中表述出来,针对每一个用例我们可以用事件流来描述这一对话的细节内容。如在ATM系统中的"提款"用例可以用事件流表述如下:

  提款-基本事件流
   1. 用户插入信用卡
   2. 输入密码
   3. 输入提款金额
   4. 提取现金
   5. 退出系统,取回信用卡

  但是这只描述了提款用例中最顺利的一种情况,作为一个实用的系统,我们还必须考虑可能发生的各种其他情况,如信用卡无效、输入密码错、用户帐号中的现金余额不够等,所有这些可能发生的各种情况(包括正常的和异常的)被称之为用例的场景(Scenario),场景也被称作是用例的实例(Instance)。在用例的各种场景中,最常见的场景是用基本流(Basic Flow)来描述的,其他的场景则是用备选流(Alternative Flow)来描述。对于ATM系统中的"提款"用例,我们可以得到如下一些备选流:

  提款-备选事件流

  备选流一:用户可以在基本流中的任何一步选择退出,转至基本流步骤5。

  备选流二:在基本流步骤1中,用户插入无效信用卡,系统显示错误并退出信用卡,用例结束。

  备选流三:在基本流步骤2中,用户输入错误密码,系统显示错误并提示用户重新输入密码,重新回到基本流步骤2;三次输入密码错误后,信用卡被系统没收,用例结束。

  …

  通过基本流与备选流的组合,就可以将用例所有可能发生的各种场景全部描述清楚。我们在描述用例的事件流的时候,就是要尽可能地将所有可能的场景都描述出来,以保证需求的完备性。

1.3 用例方法的优点

   用例方法完全是站在用户的角度上(从系统的外部)来描述系统的功能的。在用例方法中,我们把被定义系统看作是一个黑箱,我们并不关心系统内部是如何完成它所提供的功能的。用例方法首先描述了被定义系统有哪些外部使用者(抽象成为Actor),这些使用者与被定义系统发生交互;针对每一参与者,用例方法又描述了系统为这些参与者提供了什么样的服务(抽象成为Use Case),或者说系统是如何被这些参与者使用的。所以从用例图中,我们可以得到对于被定义系统的一个总体印象。

  与传统的功能分解方式相比,用例方法完全是从外部来定义系统的功能,它把需求与设计完全分离开来。在面向对象的分析设计方法中,用例模型主要用于表述系统的功能性需求,系统的设计主要由对象模型来记录表述。另外,用例定义了系统功能的使用环境与上下文,每一个用例描述的是一个完整的系统服务。用例方法比传统的SRS更易于被用户所理解,它可以作为开发人员和用户之间针对系统需求进行沟通的一个有效手段。

  在RUP中,用例被作为整个软件开发流程的基础,很多类型的开发活动都把用例作为一个主要的输入工件(Artifact),如项目管理、分析设计、测试等。根据用例来对目标系统进行测试,可以根据用例中所描述的环境和上下文来完整地测试一个系统服务,可以根据用例的各个场景(Scenario)来设计测试用例,完全地测试用例的各种场景可以保证测试的完备性。

2. 建立用例模型

   使用用例的方法来描述系统的功能需求的过程就是用例建模,用例模型主要包括以下两部分内容:

  • 用例图(Use Case Diagram)
    确定系统中所包含的参与者、用例和两者之间的对应关系,用例图描述的是关于系统功能的一个概述。
  • 用例规约(Use Case Specification)
    针对每一个用例都应该有一个用例规约文档与之相对应,该文档描述用例的细节内容。

  在用例建模的过程中,我们建议的步聚是先找出参与者,再根据参与者确定每个参与者相关的用例,最后再细化每一个用例的用例规约。

2.1 寻找参与者

   所谓的参与者是指所有存在于系统外部并与系统进行交互的人或其他系统。通俗地讲,参与者就是我们所要定义系统的使用者。寻找参与者可以从以下问题入手:

  • 系统开发完成之后,有哪些人会使用这个系统?
  • 系统需要从哪些人或其他系统中获得数据?
  • 系统会为哪些人或其他系统提供数据?
  • 系统会与哪些其他系统相关联?
  • 系统是由谁来维护和管理的?

  这些问题有助于我们抽象出系统的参与者。对于ATM机的例子,回答这些问题可以使我们找到更多的参与者:操作员负责维护和管理ATM机系统、ATM机也需要与后台服务器进行通讯以获得有关用户帐号的相关信息。


2.1.1 系统边界决定了参与者

  参与者是由系统的边界所决定的,如果我们所要定义的系统边界仅限于ATM机本身,那么后台服务器就是一个外部的系统,可以抽象为一个参与者。


  如果我们所要定义的系统边界扩大至整个银行系统,ATM机和后台服务器都是整个银行系统的一部分,这时候后台服务器就不再被抽象成为一个参与者。


  值得注意的是,用例建模时不要将一些系统的组成结构作为参与者来进行抽象,如在ATM机系统中,打印机只是系统的一个组成部分,不应将它抽象成一个独立的参与者;在一个MIS管理系统中,数据库系统往往只作为系统的一个组成部分,一般不将其单独抽象成一个参与者。

2.1.2 特殊的参与者――系统时钟

  有时候我们需要在系统内部定时地执行一些操作,如检测系统资源使用情况、定期地生成统计报表等等。从表面上看,这些操作并不是由外部的人或系统触发的,应该怎样用用例方法来表述这一类功能需求呢?对于这种情况,我们可以抽象出一个系统时钟或定时器参与者,利用该参与者来触发这一类定时操作。从逻辑上,这一参与者应该被理解成是系统外部的,由它来触发系统所提供的用例对话。


2.2 确定用例

  找到参与者之后,我们就可以根据参与者来确定系统的用例,主要是看各参与者需要系统提供什么样的服务,或者说参与者是如何使用系统的。寻找用例可以从以下问题入手(针对每一个参与者):

  • 参与者为什么要使用该系统?
  • 参与者是否会在系统中创建、修改、删除、访问、存储数据?如果是的话,参与者又是如何来完成这些操作的?
  • 参与者是否会将外部的某些事件通知给该系统?
  • 系统是否会将内部的某些事件通知该参与者?

  综合以上所述,ATM系统的用例图可表示如下,


  在用例的抽取过程中,必须注意:用例必须是由某一个主角触发而产生的活动,即每个用例至少应该涉及一个主角。如果存在与主角不进行交互的用例,就可以考虑将其并入其他用例;或者是检查该用例相对应的参与者是否被遗漏,如果是,则补上该参与者。反之,每个参与者也必须至少涉及到一个用例,如果发现有不与任何用例相关联的参与者存在,就应该考虑该参与者是如何与系统发生对话的,或者由参与者确定一个新的用例,或者该参与者是一个多余的模型元素,应该将其删除。

  可视化建模的主要目的之一就是要增强团队的沟通,用例模型必须是易于理解的。用例建模往往是一个团队开发的过程,系统分析员在建模过程中必须注意参与者和用例的名称应该符合一定的命名约定,这样整个用例模型才能够符合一定的风格。如参与者的名称一般都是名词,用例名称一般都是动宾词组等。

  对于同一个系统,不同的人对于 参与者和用例都可能有不同的抽象结果,因而得到不同的用例模型。我们需要在多个用例模型方案中选择一种"最佳"(或"较佳")的结果,一个好的用例模型应该能够容易被不同的涉众所理解,并且不同的涉众对于同一用例模型的理解应该是一致的。

2.3 描述用例规约

  应该避免这样一种误解――认为由参与者和用例构成的用例图就是用例模型,用例图只是在总体上大致描述了系统所能提供的各种服务,让我们对于系统的功能有一个总体的认识。除此之外,我们还需要描述每一个有例的详细信息,这些信息包含在用例规约中,用例模型是由用例图和每一个用例的详细描述――用例规约所组成的。RUP中提供了用例规约的模板,每一个用例的用例规约都应该包含以下内容:

  • 简要说明 (Brief Description)
    简要介绍该用例的作用和目的。
  • 事件流 (Flow of Event)
    包括基本流和备选流,事件流应该表示出所有的场景。
  • 用例场景 (Use-Case Scenario)
    包括成功场景和失败场景,场景主要是由基本流和备选流组合而成的。
  • 特殊需求 (Special Requirement)
    描述与该用例相关的非功能性需求(包括性能、可靠性、可用性和可扩展性等)和设计约束(所使用的操作系统、开发工具等)。
  • 前置条件 (Pre-Condition)
    执行用例之前系统必须所处的状态。
  • 后置条件 (Post-Condition)
    用例执行完毕后系统可能处于的一组状态。

  用例规约基本上是用文本方式来表述的,为了更加清晰地描述事件流,也可以选择使用状态图、活动图或序列图来辅助说明。只要有助于表达的简洁明了,就可以在用例中任意粘贴用户界面和流程的图形化显示方式,或是其他图形。如活动图有助于描述复杂的决策流程,状态转移图有助于描述与状态相关的系统行为,序列图适合于描述基于时间顺序的消息传递。

2.3.1 基本流

  基本流描述的是该用例最正常的一种场景,在基本流中系统执行一系列活动步骤来响应参与者提出的服务请求。我们建议用以下格式来描述基本流:

  (1) 每一个步骤都需要用数字编号以清楚地标明步骤的先后顺序。

  (2) 用一句简短的标题来概括每一步骤的主要内容,这样阅读者可以通过浏览标题来快速地了解用例的主要步骤。在用例建模的早期,我们也只需要描述到事件流步骤标题这一层,以免过早地陷入到用例描述的细节中去。

  (3) 当整个用例模型基本稳定之后,我们再针对每一步骤详细描述参与者和系统之间所发生的交互。建议采用双向(roundtrip)描述法来保证描述的完整性,即每一步骤都需要从正反两个方面来描述:(1)参与者向系统提交了什么信息;(2)对此系统有什么样的响应。具体例子请参见附录。

  在描述参与者和系统之间的信息交换时,需指出来回传递的具体信息。例如,只表述参与者输入了客户信息就不够明确,最好明确地说参与者输入了客户姓名和地址。通常可以利用词汇表让用例的复杂性保持在可控范围内,可以在词汇表中定义客户信息等内容,使用例不至于陷入过多的细节。

2.3.2 备选流

  备选流负责描述用例执行过程中异常的或偶尔发生的一些情况,备选流和基本流的组合应该能够覆盖该用例所有可能发生的场景。在描述备选流时,应该包括以下几个要素:

  (1) 起点:该备选流从事件流的哪一步开始;

  (2) 条件:在什么条件下会触发该备选流;

  (3) 动作:系统在该备选流下会采取哪些动作;

  (4) 恢复:该备选流结束之后,该用例应如何继续执行。

  备选流的描述格式可以与基本流的格式一致,也需要编号并以标题概述其内容,编号前可以加以字母前缀A(Alternative)以示与基本流步骤相区别。

2.3.3 用例场景

  用例在实际执行的时候会有很多的不同情况发生,称之为用例场景;也可以说场景是用例的实例,我们在描述用例的时候要覆盖所有的用例场景,否则就有可能导致需求的遗漏。在用例规约中,场景的描述可以由基本流和备选流的组合来表示。场景既可以帮助我们防止需求的遗漏,同时也可以对后续的开发工作起到很大的帮助:开发人员必须实现所有的场景、测试人员可以根据用例场景来设计测试用例。

2.3.4 特殊需求

  特殊需求通常是非功能性需求,它为一个用例所专有,但不适合在用例的事件流文本中进行说明。特殊需求的例子包括法律或法规方面的需求、应用程序标准和所构建系统的质量属性(包括可用性、可靠性、性能或支持性需求等)。此外,其他一些设计约束,如操作系统及环境、兼容性需求等,也可以在此节中记录。

  需要注意的是,这里记录的是专属于该用例的特殊需求;对于一些全局的非功能性需求和设计约束,它们并不是该用例所专有的,应把它们记录在《补充规约》中。

2.3.5 前置和后置条件

  前置条件是执行用例之前必须存在的系统状态,后置条件是用例一执行完毕后系统可能处于的一组状态。

2.4 检查用例模型

  用例模型完成之后,可以对用例模型进行检查,看看是否有遗漏或错误之处。主要可以从以下几个方面来进行检查:

  • 功能需求的完备性
    现有的用例模型是否完整地描述了系统功能,这也是我们判断用例建模工作是否结束的标志。如果发现还有系统功能没有被记录在现有的用例模型中,那么我们就需要抽象一些新的用例来记录这些需求,或是将他们归纳在一些现有的用例之中。
  • 模型是否易于理解
    用例模型最大的优点就在于它应该易于被不同的涉众所理解,因而用例建模最主要的指导原则就是它的可理解性。用例的粒度、个数以及模型元素之间的关系复杂程度都应该由该指导原则决定。
  • 是否存在不一致性
    系统的用例模型是由多个系统分析员协同完成的,模型本身也是由多个工件所组成的,所以我们要特别注意不同工件之前是否存在前后矛盾或冲突的地方,避免在模型内部产生不一致性。不一致性会直接影响到需求定义的准确性。
  • 避免二义性语义
    好的需求定义应该是无二义性的,即不同的人对于同一需求的理解应该是一致的。在用例规约的描述中,应该避免定义含义模糊的需求,即无二义性。

3. 系统需求

  RUP中根据FURPS+模型将系统需求分为以下几类:

  • 功能(Functionality)
  • 可用性(Usability)
  • 可靠性(Reliability)
  • 性能(Performance)
  • 可支持性(Supportability)
  • 设计约束等

  除了第一项功能性需求之外的其他需求都归之为非功能性需求。

3.1 需求工件集

  用例模型主要用于描述系统的功能性需求,对于其他的非功能性需要用其他文档来记录。RUP中定义了如下的需求工件集合。

  • 用例模型:记录功能性需求
    • 用例图:描述参与者和用例之间的关系
    • 用例规约:描述每一个用例的细节信息
  • 补充规约:记录一些全局性的功能需求、非功能性需求和设计约束等
  • 词汇表:记录一些系统需求相关的术语

  在实际应用中,除了这些工件之外,我们还可以根据实际需求灵活选用其他形式的文档来补充说明需求。并不是所有的系统需求都适保合用用例模型来描述的,如编译器,我们很难用用例方法来表述它所处理的语言的方法规则,在这种情况下,采用传统的BNF范式来表述更加合适一些。在电信软件行业中,很多电信标准都是采用SDL语言来描述的,我们也不必用UML来改写这些标准(UML对SDL存在着这样的兼容性),只需将SDL形式的电信标准作为需求工件之一,在其他工件中对其加以引用就可以了。总之,万万不可拘泥于用例建模的形式,应灵活运用各种方式的长处。

3.2 补充规约

  补充规约记录那些在用例模型中不易表述的系统需求,主要包括以下内容。

  • 功能性
    功能性需求主要在用例模型中刻画,但是也有部分需求不适合在用例中表述。有些功能性需求是全局性的,适用于所有的用例,如出错处理、I18N支持等,我们不需要在所有的用例中描述这些功能性需求,只需要在补充规约中统一描述就可以了。
  • 可用性
    记录所有可用性相关的需求,如系统的使用者所需要的培训时间、是否应附合一些常见的可用性标准如Windows界面风格等。
  • 可靠性
    定义系统可靠性相关的各种指标,包括:
    • 可用性:指出可用时间百分比(xx.xx%),系统处于使用、维护、降级模式等操作的小时数;
    • 平均故障间隔时间(MTBF):通常表示为小时数,但也可表示为天数、月数或年数;
    • 平均修复时间(MTTR):系统在发生故障后可以暂停运行的时间;
    • 精确度:指出系统输出要求具备的精密度(分辨率)和精确度(按照某一已知的标准);
    • 最高错误或缺陷率:通常表示为bugs/KLOC(每千行代码的错误数目)或bugs/function-point(每个功能点的错误数目)。
  • 性能
    记录系统性能相关的各种指标,包括:
    • 对事务的响应时间(平均、最长);
    • 吞吐量(例如每秒处理的事务数);
    • 容量(例如系统可以容纳的客户或事务数);
    • 降级模式(当系统以某种形式降级时可接受的运行模式);
    • 资源利用情况:内存、磁盘、通信等。
  • 可支持性
    定义所有与系统的可支持性或可维护性相关的需求,其中包括编码标准、命名约定、类库、如何来对系统进行维护操作和相应的维护实用工具等。
  • 设计约束
    设计约束代表已经批准并必须遵循的设计决定,其中包括软件开发流程、开发工具、系统构架、编程语言、第三方构件类库、运行平台和数据库系统等等。

3.3 词汇表

  词汇表主要用于定义项目特定的术语,它有助于开发人员对项目中所用的术语有统一的理解和使用,它也是后续阶段中进行对象抽象的基础。

4. 调整用例模型

  在一般的用例图中,我们只表述参与者和用例之间的关系,即它们之间的通讯关联。除此之外,我们还可以描述参与者与参与者之间的泛化(generalization)、用例和用例之间的包含(include)、扩展(extend)和泛化(generalization)关系。我们利用这些关系来调整已有的用例模型,把一些公共的信息抽取出来重用,使得用例模型更易于维护。但是在应用中要小心选用这些关系,一般来说这些关系都会增加用例和关系的个数,从而增加用例模型的复杂度。而且一般都是在用例模型完成之后才对用例模型进行调整,所以在用例建模的初期不必要急于抽象用例之间的关系。

4.1 参与者之间的关系

  参与者之间可以有泛化(Generalization)关系(或称为"继承"关系)。例如在需求分析中常见的权限控制问题(如下图所示),一般的用户只可以使用一些常规的操作,而管理员除了常规操作之外还需要进行一些系统管理工作,操作员既可以进行常规操作又可以进行一些配置操作。


  在这个例子中我们会发现管理员和操作员都是一种特殊的用户,他们拥有普通用户所拥有的全部权限,此外他们还有自己独有的权限。这里我们可进一步把普通用户和管理员、操作员之间的关系抽象成泛化(Generalization)关系,管理员和操作员可以继承普通用户的全部特性(包括权限),他们又可以有自己独有的特性(如操作、权限等)。这样可以显著减速少用例图中通讯关联的个数,简化用例模型,使之更易于理解。


4.2 用例之间的关系

  用例描述的是系统外部可见的行为,是系统为某一个或几个参与者提供的一段完整的服务。从原则上来讲,用例之间都是并列的,它们之间并不存在着包含从属关系。但是从保证用例模型的可维护性和一致性角度来看,我们可以在用例之间抽象出包含(include)、扩展(extend)和泛化(generalization)这几种关系。这几种关系都是从现有的用例中抽取出公共的那部分信息,然后通后过不同的方法来重用这部公共信息,以减少模型维护的工作量。

4.2.1 包含(include)

  包含关系是通过在关联关系上应用<<include>>构造型来表示的,如下图所示。它所表示的语义是指基础用例(Base)会用到被包含用例(Inclusion),具体地讲,就是将被包含用例的事件流插入到基础用例的事件流中。


  包含关系是UML1.3中的表述,在UML1.1中,同等语义的关系被表述为使用(uses),如下图。


  在ATM机中,如果查询、取现、转帐这三个用例都需要打印一个回执给客户,我们就可以把打印回执这一部分内容提取出来,抽象成为一个单独的用例"打印回执",而原有的查询、取现、转帐三个例都会包含这个用例。每当以后要对打印回执部分的需求进行修改时,就只需要改动一个用例,而不用在每一个用例都作相应修改,这样就提高了用例模型的可维护性。


  在基础用例的事件流中,我们只需要引用被包含用例即可。

  查询-基本事件流

  1. 用户插入信用卡

  2. 输入密码

  3. 选择查询

  4. 查看帐号余额

  5. 包含用例"打印回执"

  6. 退出系统,取回信用卡

  在这个例子中,多个用例需要用到同一段行为,我们可以把这段共同的行为单独抽象成为一个用例,然后让其他的用例来包含这一用例。从而避免在多个用例中重复性地描述同一段行为,也可以防止该段行为在多个用例中的描述出现不一致性。当需要修改这段公共的需求时,我们也只需要修改一个用例,避免同时修改多个用例而产生的不一致性和重复性工作。

  有时当某一个用例的事件流过于复杂时,为了简化用例的描述,我们也可以把某一段事件流抽象成为一个被包含的用例。这种情况类似于在过程设计语言中,将程序的某一段算法封装成一个子过程,然后再从主程序中调用这一子过程。

4.2.2 扩展(extend)

  扩展(extend)关系如下图所示,基础用例(Base)中定义有一至多个已命名的扩展点,扩展关系是指将扩展用例(Extension)的事件流在一定的条件下按照相应的扩展点插入到基础用例(Base)中。对于包含关系而言,子用例中的事件流是一定插入到基础用例中去的,并且插入点只有一个。而扩展关系可以根据一定的条件来决定是否将扩展用例的事件流插入基础用例事件流,并且插入点可以有多个。


  例如对于电话业务,可以在基本通话(Call)业务上扩展出一些增值业务如:呼叫等待(Call Waiting)和呼叫转移(Call Transfer)。我们可以用扩展关系将这些业务的用例模型描述如下。



 

  在这个例子中,呼叫等待和呼叫转移都是对基本通话用例的扩展,但是这两个用例只有在一定的条件下(如应答方正忙或应答方无应答)才会将被扩展用例的事件流嵌入基本通话用例的扩展点,并重用基本通话用例中的事件流。

  值得注意的是扩展用例的事件流往往可以也可抽象为基础用例的备选流,如上例中的呼叫等待和呼叫转移都可以作为基本通话用例的备选流而存在。但是基本通话用例已经是一个很复杂的用例了,选用扩展关系将增值业务抽象成为单独的用例可以避免基础用例过于复杂,并且把一些可选的操作独立封装在另外的用例中。

4.2.3 泛化(generalization)

  当多个用例共同拥有一种类似的结构和行为的时候,我们可以将它们的共性抽象成为父用例,其他的用例作为泛化关系中的子用例。在用例的泛化关系中,子用例是父用例的一种特殊形式,子用例继承了父用例所有的结构、行为和关系。在实际应用中很少使用泛化关系,子用例中的特殊行为都可以作为父用例中的备选流存在。


  以下是一个用例泛化关系的例子,执行交易是一种交易抽象,执行房产交易和执行证券交易都是一种特殊的交易形式。

  用例泛化关系中的事件流示例如下:


4.3 调整用例模型

  用例模型建成之后,我们可以对用例模型进行检视,看是否可以进一步简化用例模型、提高重用程度、增加模型的可维护性。主要可以从以下检查点(checkpoints)入手:

  • 用例之间是否相互独立?如果两个用例总是以同样的顺序被激活,可能需要将它们合并为一个用例。
  • 多个用例之间是否有非常相似的行为或事件流?如果有,可以考虑将它们合并为一个用例。
  • 用例事件流的一部分是否已被构建为另一个用例?如果是,可以让该用例包含(include)另一用例。
  • 是否应该将一个用例的事件流插入另一个用例的事件流中?如果是,利用与另一个用例的扩展关系(extend)来建立此模型。

5. 管理用例模型复杂度

  一般小型的系统,其用例模型中包含的参与者和用例不会太多,一个用例图就可以容纳所有的参与者,所有的参与者和用例也可以并存于同一个层次结构中。对于较复杂的大中型系统,用例模型中的参与者和用例会大大增加,我们需要一些方法来有效地管理由于规模上升而造成的复杂度。

5.1 用例包

  包(Package)是UML中最常用的管理模型复杂度的机制,包也是UML中语义最简单的一种模型元素,它就是一种容器,在包中可以容纳其他任意的模型元素(包括其他的包)。在用例模型中,我们可以用构造型(Sterotype)<<use case>>来扩展标准UML包的语义,这种新的包叫作用例包(Use Case Package),用于分类管理用例模型中的模型元素。

  我们可以根据参与者和用例的特性来对它们进行分类,分别置于不同的用例包管理之下。例如对于一个大型的企业管理信息系统,我们可以根据参与者和用例的内容将它们分别归于人力资源、财务、采购、销售、客务服务这些用例包之下。这样我们将整个用例模型划分成为两个层次,在第一层次我们看到的是系统功能总共分为五部分,在第二层次我们可以分别看到每一用例包内部的参与者和用例。


  个用例模型需要有多少个用例包取决你想怎么样来管理用例模型的复杂度(包括参与者和用例的个数,以及它们之间的相互关系)。UML中的包其实就类似于文件系统中的目录,文件数量少的时候不需要额外的目录,文件数量一多就需要有多个目录来分类管理,同样一组文件不同的人会创建不同的目录结构来进行管理,关键是要保证在目录结构下每一个文件都要易于访问。同样的道理存在于用例建模之中,如何创建用例包以及用例包的个数取决于不同的系统和系统分析员,但要保证整个用例模型易于理解。

5.2 用例的粒度

  我的系统需要有多少个用例?这是很多人在用例建模时会产生的疑惑。描述同一个系统,不同的人会产生不同的用例模型。例如对于各种系统中常见的"维护用户"用例,它里面包含了添加用户、修改用户信息、删除用户等操作,这些操作在该用例的事件流可以表述成为基本流的子事件流(subflow)。


 

  维护用户-基本事件流

  该基本流由三个子事件流构成:

  (1) 添加用户子事件流

  (2) 修改用户 子事件流

  (3) 删除用户子事件流

  但是你也可以根据该用例中的具体操作把它抽象成为三个用例,它所表示的系统需求和单个用例的模型是完全一样的。


  应该如何确定用例的粒度呢?在一次技术研讨会上,有人问起Ivar Jacoboson博士,一个系统需要有多少个用例?大师的回答是20个,当然他的意思是最好将用例模型的规模控制在几十个用例左右,这样比较容易来管理用例模型的复杂度。在用例个数大致确定的条件下,我们就很容易来确定用例粒度的大小。对于较复杂的系统,我们需要控制用例模型一级的复杂度,所以可以将复杂度适当地移往每一个用例的内部,也就是让一个用例包含较多的需求信息量。对于比较简单的系统,我们则可以将复杂度适度地曝露在模型一级,也就是我们可以将较复杂的用例分解成为多个用例。

  用例的粒度不但决定了用例模型级的复杂度,而且也决定了每一个用例内部的复杂度。我们应该根据每个系统的具体情况,因时因宜地来把握各个层次的复杂度,在尽可能保证整个用例模型的易理解性前提下决定用例的大小和数目。

5.3 用例图

  用例图的主要作用是描述参与者和用例之间的关系,简单的系统中只需要有一个用例图就可以把所有的关系都描述清楚。复杂的系统中可以有多个用例图,例如每个用例包都可以有一个独立的用例图来描述该用例包中所有的参与者和用例的关系。

  在一个用例模型中,如果参与者和用例之间存在着多对多的关系,并且他们之间的关系比较复杂,如果在同一个用例图中表述所有的参与者和用例就显得不够清晰,这时我们可创建多个用例图来分别表示各种关系。


  如果想要强调某一个参与者和多个用例的关系,你就可以以该参与者为中心,用一个用例图表述出该参与者和多个用例之间的关系。在这个用例图中,我们强调的是该参与者会使用系统所提供的哪些服务。


  如果想要强调某一个用例和多个参与者之间的关系,你就可以以该用例为中心,用一个用例图表述出该用例和多个参与者之间的关系。在这个用例图中,我们强调的是该用例会涉及到哪些参与者,或者说该用例所表示的系统服务有哪些使用者。


  总之在用例建模过程中,你可以根据自己的需要创建任意多个用例图,用不同的用例来强调参与者和用例之间不同的关系。但是最重要的是要考虑整个用例模型的可理解性,如果可以用一个用例图把意思表述清楚,就不要再用第二个,因为越是简洁的模型越易于理解。

参考资料



样例代码


Jeffrey Friedl, Mastering Regular Expressions, O’Reilly


Mendel Cooper, Advanced Bash-Scripting Guide


Michael Jang, Mastering Redhat 9

 

关于作者
傅纯一,IBM中国有限公司软件部Rational中国区技术销售经理