找回密码
 立即注册

用例的艺术:需求描述的精准之道

#产品观察# 2024-10-4 09:17 158人围观 产品观察

用例(Use Case)是一种描述需求的方法。运用用例这种方法来描述需求称之为用例建模。用例也是UML规范中的一种标准化的需求表达方式,其中比较有名的RUP(Rational Unified Process,统一软件开发过程)就是以用例来驱动的。
值得一提的是,虽然RUP被认为是一种重量级的软件管理过程,而越来越多的软件开发开始采用敏捷方法来响应瞬息万变的需求变化。但是用例作为一种描述需求的方式,其理念和方法论对我们分析需求还是有一定的帮助。
从表达方式上看,用例相对时序图、流程图等需求表达方式,更加面向对象和面向设计。通常情况下,用例比较容易让没有受过专业培训的人员接受。用例可以作为一种跟用户或者行外人员陈述需求的方式。

01 用例是什么

用例是一种描述需求的方法,用例描述了在不同的条件下,系统对参与者的请求做出的响应。用例通常通过一个参与者(Actor)(谁?)向系统做出请求(要做什么?),系统根据参与者的请求,在不同的条件下,执行某一行为序列(系统怎么满足?)。每一个行为序列可以称之为一个场景(Scenario),一个用例包含多个场景。场景也可以称之为用例的一个实例(Instance)。

02 用例的组成
正式的用例应该包括:用例名、概述、范围、级别、主参与者、项目相关人员和利益、前置条件、最小保证、成功保证、触发事件、主成功场景、扩展场景和相关信息等等。
各个组成部分的意义如下:
  • 用例名(Title):<用例目标>
  • 概述(Goal in Context):<要描述用例流程和目标>
  • 范围(Scope):<设计范围>
  • 级别(Level):<概要、用户目标、子功能>
  • 主执行者(Primary Actor):<主执行者或角色>
  • 项目相关人员和利益(Stakeholders and Interests):<项目相关人和主要利益列表>
  • 前置条件 (Precondition):<已经达到的条件>
  • 最小保证(Minimal Guarantees):<用例必须执行的信息>
  • 成功保证(Success Guarantees):<目标完成时执行的信息>
  • 触发事件(Trigger):<触发了用例的事件>
  • 主成功场景(Main Success Scenario):<目标完成的主要步骤>
  • 扩展(Extensions):<可能出现的其他步骤,包括异常情况>

03 用例的格式
用例可以用于不同的目的,如:
  • 描述需求,便于和用户沟通需求。
  • 描述业务的过程,指导项目开发。
  • 记录需求讨论过程,并最终文档化。
不同的编写目的导致了用例在编写过程中有可能出现不同的侧重点。
在不同的团队情况也可能导致用例书写的不一样。比如在一个大型的开发项目组里,就需要严格的按照用例范例进行描述,而在一个小型的沟通频繁的项目组里,则可以采用一种比较简单的描述方式。
上文提到的是比较常见的组成部分。事实上,用例的格式并没有硬性规定,在必要时可以增减里面的信息。具体用例需要包括哪些信息,有不同的流派。有兴趣可以查看相关资料,这里不展开讲。
一句话概括: 你的用例不是我的用例,只有适合的用例才是最好的

04 需求和用例
用例用于表述需求,但是有两点要注意的:
  • 用例就是需求。 不需要将用例转化成其他的需求表达方式。用例可以完整的描述系统需求。
  • 用例不一定是所有需求。 用例只是需求的一部分,用例并不描述外部接口、数据格式、业务规则、性能、可维护性等需求。

05 用例组成部分
1. 用例名(Title)用例名用于标识一个用例,便于汇总和阅读。
规则:使用主动语态的动宾短语来描述。
一般情况下,建议使用主动语态的动宾短语来描述用例的目标。如:“查找商品”、“加入购物车”。在某些情况下,如需要更准确的表示出一个用例,可以加入定语进行修饰,如:“用户清除购物车”、“管理员清除购物车”。
规则:以主参与者为对象进行描述。
用例的描述需要以主参与者为对象进行描述,如可以使用“支付订单”(以主参与者为对象),而不是“收取订单费用”(以系统为对象)。
2. 范围(Scope)用例的范围能让我们对系统的边界和讨论的需求有一个基础的语境,不同的设计范围可能会导致我们需要讨论的参与者、场景都会不一样。简单来讲,就是为我们讨论的系统划定一个范围确定我们讨论的界线。
例如我们要讨论一个用户的下单行为。如果以整个企业为范围,其项目的相关人员为用户、第三方服务者(如快递等)。但如果以系统为范围,其项目相关人员还应该包括企业内部的系统管理员、客服等。
所以,在编写用例时需要搞清楚,我们的用例的范围是什么,这样可以对用例讨论的问题达成一个共识。
1)功能范围
在讨论用例的设计范围时,需要先确定系统的功能范围。Cockburn在《编写有效用例》里面推荐了一个确定功能范围的方式“内/外列表”。

确定功能范围的好处是显而易见。如,系统外部已经有了一个打印订单的系统,如果不明确区分系统的功能范围,部分开发人员有可能会对打印订单功能进行设计和实现。而事实上,这些功能是不需要设计的。
明确了功能范围后,还可以确认系统的执行人员。如上面的例子,打印订单系统将作为“打印订单”用例的辅助执行者。
2)设计范围
设计范围是在功能范围确定了之后做的。设计范围指的是我们在编写用例时讨论问题的边界和对象。我们在用例里面说的范围(Scope)如果没有特殊说明指的就是设计范围(而不是功能范围)。
下面来看一个例子,ECom公司打算做一个ESys的系统,系统里面包括了ESubSys等多个子系统。

设计范围的大小
如果以ECom为设计范围来讨论用例,我们关心的是用户对公司的需求是什么,公司以什么样的形式满足用户的请求。如果有外部公司,则还要考虑外部公司与公司之间往来的业务是什么。
如果以ESys为设计范围来讨论用例,我们更关心用户向系统发起的请求和系统对请求的响应。同时,如果以ESys做范围的时候,企业内部的员工也成了用例的执行者,我们还应该考虑员工对系统的请求。
确定用例范围,能很好的对其我们要讨论的问题是什么,界定我们讨论问题的范围,给用例一个语言环境。
规则:设计范围是一个简单的专有名称。
用例的范围应该是一个简单的专用名词,简单说明一下用例讨论的范围界线。如,上面的例子中范围可以直接用“ECom”、“ESys”、“ESubSys”来表示。
3. 主执行者(Primary Actor)主执行者是系统相关人员中,请求系统做出响应的人或物。主执行者是对系统请求的发起者,主执行者可以不是直接操作系统的相关人员
其中一种情况下是主执行者通过另一个系统操作相关人员对系统进行操作。如,客户致电客服查询异常订单的场景。客户并没有直接通过系统进行查询。
另一种情况是定时触发任务。如客户希望系统定时执行一个任务,那么最终执行系统的相关人员是系统本身。
虽然识别出主执行者很重要,可是在有些时候主执行者也没那么重要
在编写用例时,识别出主执行者,可以从执行者角度出发,充分梳理系统需求。我们还可以主执行者的特点来设计系统的交互。如下表,主执行者概括表:

在多数情况下,我们开始编写用例开始后,主执行者就变得没那么重要了。例如,当我们在设计查询订单用例时,无论是管理员、经理、客服甚至是其他的公司职位,在查询订单这个用例上并没有特别的差异。这个时候,主执行者具体是谁已经不重要了。
规则:用例的主执行者可以是执行者或者执行者角色。
在上述情况下,我们会将部分主执行者一般化的方式,创建一个“角色-执行者对应表”。在上述用例里,我们将管理员、经理等一般化为一个操作角色——订单管理者。我们在描写用例时,以角色作为主执行者即可。
4. 概述(Goal in Context)概述主要用于描述用例的目标,也就是用户需要完成的目标。
规则:使用自然语言描述。
尽量使用自然的语言阐述用户要完成目标时,用户会做什么事情。
规则:描述用例实现什么,而不描述系统步骤。
只需要讲清楚用例需要完成的事情即可,这里不需要描述系统步骤或者用户的具体操作流程。
如:“用户选择一件需要购买的商品后,可以将商品加入购物车,然后在购物车里面提交订单。用户也可以不需要加入购物车,直接购买选中的商品。”概述并不需要描述具体的系统操作,在这里并没有描述“点击加入购物车按钮”等系统的操作细节。
5. 相关人员和利益(Stakeholders and Interests)项目的相关人员是指对系统有特定利益的参与者。相关人员不一定是人,也可以是一个外部系统、一个组织等。
所以能成为项目相关人员的有可能是:
  • 使用或关注系统的外部人或物。
  • 用例的主执行者。
  • 用例的辅助执行者。
  • 系统内部执行者。
  • 被设计的系统本身。
1)主执行者
主执行者是发起执行用例的相关人员。
2)辅助执行者
辅助执行者是为被设计的系统执行服务的的外部执行人员。辅助执行者可以是另外一个系统、也可以是一个人或组织。如,一台打印机,为系统打印各种票据。再如,快递公司,为系统提供快递服务,并提供物流信息。
3)内部执行者
内部执行者是在系统内部关注系统利益的相关人员。
4)被设计的系统
被设计的系统本身有时候对自己也是有特定利益的。
对于相关人员,有几点需要说明:
  • 系统相关人员有可能不直接和系统交互。 例如,公司管理者,可能不会亲自操作系统,可是对系统运行的状况和其他运营数据却是十分关注的。这些相关人员在系统操作步骤中可能不会出现,但是用例还是需要描述对这部分相关人员的利益。
  • 关注“沉默的”相关人员。 系统相关人员有时候并不是那么明显。比如上文提到的,有些相关人员并不是直接操作系统的人员。往往是这些“沉默”的相关人员考虑不足,正是系统后期需求频繁改动的原因之一。
规则:相关人员和利益用以对应列表的方式书写。
使用”<相关人员>:<利益>”的方式,描写相关人员和其关注的利益。
6. 级别(Level)在编写用例过程中,我们有时会具体描述一个用户的需求(如用户购买商品),有时候会描述一个系统的具体功能(如用户登陆),有时候会描述一个流程(如购买商品并获得商品的流程)。在编写用例的时候,知道用例所处的位置,对我们编写和理解用例有很大的帮助。
我们将用例级别从总到分划分成了三个层次:概要、用户目标、子功能。
1) 用户目标
用户目标是指主执行者使用系统期望获得的目标。用户目标是我们编写用例的重点。用户目标描述了主执行者通过系统“做一件什么事”,以及做完这件事后“用户能获取什么利益”。
用户目标应该是主执行者一次执行系统获取利益的过程。所以,不是一次执行所能完成的目标,或者用户不能获得利益的需求不能称为用户目标。
如,购买一个商品的流程,这个从下订单到快递需要几天的过程,所以不能称为一个用户目标。再如,用户登陆,用户登陆并不能获取什么利益,所以也不能称为一个用户目标。用户下单这个操作,可以作为一个用户目标。
2) 概要
概要层次可以包含多个用户目标,概要目标执行周期比用户目标更长,可以是一个几天、几个月甚至更长的过程。概要目标有三个目的:
  • 为用户目标提供一个运行的语境。如,明确用户目标是在讨论下单流程。
  • 显示用户目标之间的前后顺序。如,明确下单用例、查询快递用例、签收订单用例之间的前后顺序。
  • 作为多个用户目标的汇总。如,下单流程汇总了多个相关的用户目标。
3) 子功能
子功能层次是用户目标在执行过程中会执行到的目标。如,一次登陆,一次订单打印等。也有可能存在多个用户目标共用一个子功能,如查找商品、查找订单等。
子功能用例的存在是为了用户目标用例增加可读性而存在的。在实际编写过程中,不到迫不得已,不要设计子功能层出用例
规则:层次只有三个选项:概要、用户目标、子功能。
用例的层次只能是概要、用户目标、子功能三个之中的一个层次。
7. 前置条件(Precondition)前置条件是我们在用例执行之前期望必须成功的条件。在用例编写过程中,可以不对该条件进行检查和讨论。如,“下订单”必须依赖于“用户已经登陆”这个前置条件。
规则:前置条件必须是用例执行前我们期望一定成功的条件。
要预防将那些并不是必须条件的条件写入前置条件。如,取消订单并不依赖于用户下单成功,事实上,用户可以将下单不成功(例如支付失败)的订单取消掉。而订单下单是否成功这个条件是需要在用例里面对这个条件进行检查并执行不通过动作的。
8. 最小保证(Minimal Guarantees)最小保证是用例执行无论是否成功都会被执行的保证。虽然,用例无论执行成功与否,最小保证总会被执行。但是,最小保证更多的是为用例执行失败情况下,为用例相关人员提供的利益保证。最小保证可以有多个。
一个常见的最小保证例子是“系统将用户执行记录日志”,就算用例执行失败,用户的操作也将会被记录到日志里面。
9. 成功保证(Success Guarantees)成功保证是指用例执行成功后,用户所能得到的利益保证。相关人员的利益能否得到保证,是用例执行成功的判定条件。成功保证可以有多个。
例如,在下订单用例中,用户下单成功后,必须保证“订单被创建,并提交到后台处理。”
10. 触发事件(Trigger)触发事件是指用例启动的事件,用例将通过触发事件,开始一步一步执行。
规则:触发事件是跟系统交互的第一个操作。
以用户下单用例为例子,用户决定要购买商品后,在系统中查找商品并下单。那么“用户决定要购买商品”并不能作为用例的触发事件,事实上,用户更系统的交互是从“查找商品”开始的,所以“用户查找商品”才是用例的触发事件。
我们讨论用户跟系统交互时,还应该注意我们讨论的系统的范围。特别是当主执行者不是直接操作软件系统的场景时,更应该明确系统范围。如,“用户致电客户经理下单”这样的场景下,我们的系统范围并不能限定在软件系统范围内,这是系统范围是公司。所以,“用户致电客户经理”跟我们系统交互的第一步,所以可以成为“触发事件”。
11. 主成功场景(Main Success Scenario)主成功场景是用例从触发事件开始,一步一步执行,最终满足用例利益的步骤集合。
主成功场景应该包括以下信息:
  • 两个执行者之间的交互。如,用户提交了订单。
  • 为保证主成功场景得以继续的确认。如,系统确认用户密码。
  • 主成功场景推进过程中的内部变化。如,系统扣除用户账户余额。
执行步骤应该有一些简单的规则:
规则:使用简单语法。
使用简单语法结构:
例如:
规则:准确描述执行者之间的切换。
执行步骤需要准确描述步骤执行过程中,执行者之间的切换。如,“用户致电客户代表”,我们可以知道步骤已经从用户切换到了客户代表。
但是,有时候在执行者明确的情况下,也有可能不会出现在句子中。如,“用户输入密码”,我们也可以知道这个步骤的执行者已经从用户切换到了系统。我们不必使用“用户向系统输入密码”这种冗余的描述方式。
规则:从系统外去描述步骤。
不应该从系统内部,或者全部以系统角度去考虑而已。而应该从系统外去描述步骤。
如果从系统内部去描述步骤,可能会写成:
如果在系统外部去描述步骤,则表述成:
规则:显示过程向前推移。
一些小的步骤只能完成少数工作,有时候这些工作并不能很好的描述过程在向前推移。如,“用户点击了确定按钮”。这个步骤并不能很好的描述过程在向前推移,用户的真实目的是登陆系统,随着用户登陆系统,用例步骤可以继续往下执行。
规则:显示执行者的意图,而不是动作。
执行者通常是通过操作系统执行一个动作的,在描写用例时,容易将用户动作和执行者的意图搞混。
例如: 1. 系统要求用户输入身份信息 2. 用户输入用户名密码 3. 用户点击确定按钮 4. 系统确认用户身份信息 ……
用例过多描述了系统操作界面和用户的动作,如“要求用户输入身份信息”,这个并执行者的意图,而只是一个交互动作。
我们可以缩减描述用户动作的步骤,将用例改成:
规则:包含合理的活动集。
描述步骤的时候,并不一定要求每个步骤之包括一个活动。根据需要可以将部分活动集合在一个步骤里面。
如:
这个步骤也可以描述成两个步骤:
用例的描述方式以简单,有效为主,有时候并不拘泥于具体的方式。事实上很多开发团队都形成了自己的用例编写规范。
规则:步骤描述成功的场景,而不要体现可能的失败。
主成功场景的步骤描述的是成功的步骤。例如:
如果这样编写步骤,我们将要继续考虑“如果判断正确……”,“如果判断失败……”。但是在主成功场景的步骤中,是不体现失败的步骤的。所以,需要将步骤改成
如果如果系统验证失败怎么办?这部分信息放到扩展里面描述。下文会详细说明,这里不展开。
规则:当步骤不连续执行是,可以加入时间限制。
多数情况下,步骤是一步接一步执行的。可在某些时候,可以这样描述:
规则:一个步骤可以涉及多个相关人员。
我们有时候需要通过一个系统向另一个系统发起一个执行动作,可以写成:
规则:可以反复执行一个或多个步骤。
有时用户会反复执行其中一个或多个步骤,这时候需要在步骤中增加一定的描述。如:
1. 用户查找一个商品
2. 用户将商品加入到购物车中。用户可以重复1~2步,直至用户完成商品选购。
3. 用户选中购物车中的商品下单
……
12. 扩展(Extensions)扩展是主成功场景的分支,是指主成功场景在一些其他条件下会完成的不同动作。请注意,使用“扩展”而非“异常”或“错误”,事实上扩展包括了成功和失败两种可能的条件。其基本的逻辑是,在执行主成功场景时,如果系统……(检测到意外),那么,……(做一些事情)。
常见的有可能出现扩展的场景如下:
  • 另一种可能出现的成功路径。(如:用户设置了自动登陆)
  • 执行者操作错误。(如:用户输入的密码错误)
  • 执行者无任何操作。(如:用户输入超时)
  • 需要系统确认的场景。(如:系统确认用户余额足够)
  • 辅助执行者或其他相关人员反馈失败。(如:打印机执行打印错误)
  • 检测到内部错误,并可能产生外部可见的结果。(如:写数据失败)
  • 关键性能指标不达标。(如:系统超过15秒没有返回成功)
在这些场景出现后,我们应该在扩展中描述这些场景处理方式,然后回到主成功场景或者退出用例。
扩展是针对主成功场景的,所以我们写编写扩展的时候,需要用编号来表明扩展的对应关系:
主成功场景如下:
扩展如下:
如果是每个步骤都可能会触发的扩展,可以用”*“号来表示,如:
或者如果是某些步骤触发的共有条件,可以加上步骤来表示,如:
规则:从系统检测到的角度去描述扩展条件。
扩展条件应该是系统能检测到的条件,而不是发生了什么。如,用户忘记密码了,系统不可能检测到用户是否密码或者是其他的什么原因。从系统检测到的角度去描述,系统只能检测到用户输入错误的密码或者用户输入超时。
规则:合理化合并扩展条件。
扩展条件事实上无需枚举出所有的可能出现的场景,和合理的范围内,我们可以将一些扩展条件合并成等价项。判断等价项,有两个标准:
  • 系统可以检测到的条件。
  • 系统必须完成的条件。
例如,用户输入密码的步骤里面,用户可以忘记密码输入错误,也可以手误输入错误或者其他的可能性,这些条件都是系统不可以检测的条件。首先,将这些条件转换成系统可以测试的条件:密码输入错误。转换后,所有条件就可以合并成一个了。
在来看一下系统可以完成的条件,如,密码输入错误、用户名错误、用户名不存在等,我们系统的处理都是“提示用户名或密码错误或不存在”。这时候可以将条件合并成“系统检测到用户名或密码输入错误”。
还有一种情况,如果在低层级(如子功能级别)用例已经完整描述了扩展,那么在其高级别(如用户目标级别)用例,可以不用重复冗余描述。比如,在子功能级别用例“保存数据”里面已经完整描述了保存过程中可能出现的各种扩展条件,那么在其上级用例里就可以不用描述了。

06 总结
用例还能以用例图的方式来表示。本文主要是通过用例的关注点和用例的组成来探讨一下一种需求的描述方式,所以就不对用例图召开介绍了。有兴趣的同学可以自行参考其他资料了解。
在敏捷开发越来越受到推崇的今天,用例这种相对较“重”的需求分析和表达模式越来越少的被人使用。当是我们通过研究用例的关注点和分析方式,其很多思想还是可以借鉴到我们日常的需求分析当中的。
更详细的内容可以查看《编写有效用例》(Writing Effective Use Cases )作者是 Alistair Cockburn。
本文来源 @林海舟 ,未经许可,禁止转载