文章目录
绪论
不要单独建模…小组成员要轮流画草图,以使每个人都参与其中
并行地创建模型.例如在一块白板勾勒UML动态视图的交互图,同时在另一白板上勾画出补充性的UML静态视图的类图
开发者应该为自己进行OO设计建模,而不是创建模型图后交给其他编程者去实现–这是非敏捷的面向瀑布的方法
在白板上通过绘制UML草图来建模是我(和众多开发者)多年来热衷的方式.然而,本书中大多数UML图给人的印象并非如此,因为这些图是为了提高可读性而使用工具精心绘制的.
看来作为图的目的有两种,便于表达和便于接受,这样看来对于思考过程中的图应该就不应该有CAD这样精确的绘制,所以有sketchup
初始阶段
在早期迭代中解决高风险和高价值的问题
这样看来,消除风险也是很有价值的
不断地让用户参与评估,反馈和需求.
在书中理想的情况下,每次发布就很快得到反馈,从而知道下一个迭代,实际情况恐怕没这么快,只能间隔迭代
从上图可以看出,在一个项目的不同阶段,工作内容比例是不一样的,但是对于endless的产品迭代而言,当进入稳定期后就持续不变了.
设计包装练习: vision box exercise是Jim Highsmith在Cutter Summit中描述的用于设想陈述的一种有效方法.即团队分组对产品的包装盒进行设计,包括包装盒正面的产品名称,图案和关键卖点,以及背面的详细特性描述和运行需求.
记点投片表决: dot voting是一种设置优先级的技巧…通常每人获得的选票为列表选项数量的四分之一…其缺点是采纳了主流意见,而疏远了持非主流观点的成员,可能在今后的沟通中遗留危机
下列列出的是一些迹象,表明你并没有理解以敏捷精神采用迭代开发和UP的真正含义
- 在开始设计或实现之前试图定义大多数需求.
- 在编程之前花费数日或数周进行UML建模
- 认为初始阶段=需求阶段,细化阶段=设计阶段,构造阶段=实现阶段
- 认为细化的目的是完整仔细地定义模型,以能够在构造阶段将其转换为代码
- 坚信合适的迭代时间长度为三个月之久,而不是三周
- 认为采用UP就意味着要完成大量可能的活动和创建大量的文档
- 试图对项目从开始到结束制定详细的计划
在石油行业中,勘探一个新地域时要经历以下几个步骤:
- 确定是否已有足够的证据或业务案例来证明可以进行勘测钻探
- 如果有,则进行测量和钻探
- 提供油田范围和预算信息
- 其他更多的步骤…
初始阶段制品的样例
- 设想和业务用例
- 用例模型
- 补充性规格说明
- 词汇表
- 风险列表和风险管理计划
- 原型和概念验证
- 迭代计划
- 阶段计划和软件开发计划
- 开发案例
注:在本阶段,只完成部分制品.后续迭代中将反复对其精化
不难想到,按照敏捷的思想,上面交付的产物略显多了点
总的来说初始阶段不是用来做规整需求或者设计,这些都是在细化的过程中逐渐完善的.初始阶段更像是一个交接仪式,从外部的,大环境的诉求内化为系统的要求,其目的是让各种外部人员不再参与(或者不再作为主体参与)
用例
这也正是敏捷建模的观点:建模的最大价值是增强理解,而非记录可靠的规格说明.正如艾森豪威尔将军所说:“在即将进行作战的时候,我经常发现之前制定的计划根本排不上用场,但制定计划这项工作却是必不可少的”
文中用了大量篇幅讲解用例,毕竟这是让用户参与的重要途径,而用户参与又是软件开发成功的保证.令人惊讶的是作为一个将作图的书在这里降低了图的价值而强调文字的作用,并且指出(也许是译者)图形化的用例使得沟通变得困难.而文字的用例更加结构化
用例有几种,首先最重要的是主成功场景,比如出售商品,其次是非正式场景,比如退货,最后是各种奇葩情况的处理,往往是各种如果,比如信用卡失效.
fully dressed use case
- 用例名称
以动词开始- 范围
要设计的系统- 级别
“用户目标"或者是"子功能”- 主要参与者
调用系统,使之交付服务- 涉众及其关注点
关注该用例的人及其需要- 前置条件
开始前必须为真的条件- 成功保证
成功完成必须满足的条件- 主成功场景
典型的,无条件的,理想方式的成功场景- 扩展
成功或失败的替代场景- 特殊需求
相关的非功能需求- 技术和数据变元表
不同的IO方法和数据格式- 发生频率
影响对实现的调查,测试和时间安排- 杂项
例如未决问题
一个完整的例子可以参见书,里面提到了受众展现出真实软件需求的一面
用例的几个部分的具体含义
- 范围
范围界定了所要设计的系统.文中把用例分为两类系统用例和业务用例,文中强调的是系统用例,而按照我的理解,系统用例的范围就是系统的名称 - 级别
按照文中说法,级别可以有用户目标级别和子功能级别两类,user-goal leve是常规的级别,而subfunction-level只是将公共部分抽取出来而已.文中认为user-goal level=Elementary Business Process - 主要参与者
- 涉众及其关注点列表
文中非常强调这一点,但是我认为这一点是困难的.因为这里陷入了一个经典的死循环:如果你不通知我我怎么知道是否需要,如果你不告诉我你是否需要我怎么知道是否要通知你,这里恐怕要头脑风暴下想下所有涉及的人.只有有了所有涉及的人(有些人是隐藏起来的,有些是在后面的流程中才参与进来)才能把他们的诉求都考虑进来.
使用参与者的目标而不是系统的任务可以更加明确,避免遗漏.
而最终,这一切的关注点都要以涉众可以感知的形式明确
询问工作目标比询问工作本身更为适宜 - 前置条件
这里是一些需要注意的地方,有些不言自明的地方无需指出(例如电力供应),更一般的情况应当将前置条件理解为另一个系统的后置条件 - 后置条件
- 主成功场景
- 扩展(替代流程)
扩展是重要的,并且通常占据了文本的大部分篇幅
扩展连同主成功场景主要描述了执行路径,对于一些额外的要求(例如顾客要求显示商品描述和价格)属于可用性需求
UI vs Essential
文中建议对目标(例如登录)本身的目标(例如标识身份并被认证)进行思考,目标是有目标,目标的目标也有目标,那么尽头是哪里?无UI的情况.下面是本质风格的准则:屏除用户界面并且关注参与者的意图
但是这个是有难度的,每一次向本质的追寻,往往意味着我们有了更多的外延用例.例如出现了生物识别的方式后也许我们才想到使用口令登录不是必须的.文中比较了两种风格的用例
- 本质风格
- 管理员标识自己的身份
- 系统对此身份进行认证
- 具体风格
- 管理员在对话框输入ID和口令(见图3)
- 系统对管理员进行认证
- 系统显示"编辑用户"窗口(见图4)
黑盒
black-box use case强调的是职责.对比下面两种
- 黑盒风格
系统记录销售
- 非黑盒风格
系统将销售信息写入数据库(或者更糟糕的:系统执行insert语句)
从这里可以看出,用例更加的面向业务,更加的抽象与高层,从这个角度上来看如果一个开发人员面对用例时是有很大的自主权的,这样整个UP过程就更加的清晰,而不是一股脑的希望将各种决策前置从而避免走弯路.
系统边界
在我看来系统的边界更像是右边的情况而不是左边的情况,系统的边界难以一开始就划分清楚,而是不断精确的.这一点倒是和围棋很像
有效性
- 老板测试
你的老板问:"你整天都做了些什么?"你回答"登录系统!"你的老板会高兴吗?
这也并不意味着要忽略在老板测试中失败的用例.用户认证可能不会通过老板测试,但却是重点和难点
- EBP测试
这里和价值链很类似,去寻求小的task然后考察他们的价值 - 规模测试
用例很少有单独的活动或步骤组成,相反,用例通常包含多个步骤,在详述形式的用例通常需要3~10页的文本
下面是一些demo
- 就供应者合同进行协商
比EBP更广泛,用时更长.更适合作为业务用例而非系统用例- 处理退货
能通过老板测试- 登录
难以通过老板测试- 在游戏板上移动棋子
单一步骤,无法通过规模测试
用例图
区分人和系统
主要参与者在左侧,支持性参与者在右侧.看上去就是调用方在左侧,被调用的在右侧
特性列表
使用用例而非功能列表的一大好处是提供了场景,从而便于理解.
某些商业化销售的软件(例如新版本加入某种特性)可能更适合特性列表
另外书中举例游戏不太适合用例的形式
总的看来使用哪种方式来描述系统取决于客户如何认知系统
除了用例
应用专用的领域(业务)规则
例如如何计算商品折扣
词汇表
术语 | 定义和信息 | 格式 | 验证规则 | 别名 |
---|---|---|---|---|
UPC | …www.uc-ouncil.org | 分为几部分的12位数字代码 | 第12位是校验位 | 通用产品代码 |
另外还可能有与其他元素关系,值域
业务规则
ID | 规则 | 可变性 | 来源 |
---|
一般来说,业务规则不局限与当前软件
System Sequence Diagram
对于用例中一系列特定事件,SSD展示了直接与系统交互的外部参与者,系统(作为黑盒)以及由参与者发起的系统事件.
不用为所有场景创建SSD…相反,只需要为下次迭代所用的场景绘制SSD.同时,不应该花费太长时间来绘制SSD,用几分钟或者半小时来绘制即可
SSD是用例模型的一部分,将用例场景隐含的交互可视化
SSD与其他UP制品的关系
可以看出,SSD像是一个更概括的时序图,它屏蔽了内部的各种交互而将这个系统视为一个整体.
准则:应为每个用例的主成功场景,以及频繁发生的或者复杂的替代场景绘制SSD
为系统事件和操作命名
scan(itemID)和enterItem(itemID)这两个名字,哪个更好?
系统事件应该在意图的抽象级别而非物理输入设备级别来表达
因此,enterItem要优于scan,因为前者既捕获了操作的意图,又保留了抽象性
如此看来,不同IO设备带来的不同输入方式不应作为SSD中的划分,简单来看SSD就只有两列,用户和系统
UP阶段
- 初始
通常不会在该阶段引入SSD- 细化
大部分SSD在细化阶段创建
操作契约
后置条件
契约中重要的是后置条件,以下为一个契约的示例
操作: enteritem(itemID:ItemID,quantity:integer)
交叉引用:用例:处理销售
前置条件:正在进行的销售
后置条件:
- 创建了SaleLineItem的实例sli(创建实例).
- sli与当前Sale关联(形成关联).
- sli.quantity赋值quantity(修改属性)
- 基于itemID的匹配,将sli关联到ProductDescription(形成关联).
可以看出,其后置条件有些啰嗦,并且是无UI关联的.
另外用过去时态表达后置条件从而强调他们是观察结果
概括来说,后置条件可以分为以下三种类型:
- 创建或删除实例
- 属性值的变化
- 形成或消除关联(精确的讲,是UML链接)
消除关联会比较少见,有的时候会以删除来体现
而删除也会比较少见,这与法律及各种规定有关
下面再讲另一个例子:
操作:makeNewSale()
交叉引用:用例:处理销售
前置条件:无
后置条件:
- 创建了Sale的实例s(创建实例)
- s被关联到Register(形成关联)
- s的属性被初始化(修改属性)
在上面的例子中最后一个后置条件是模糊的,这是取决于具体场景下是否能够理解
系统操作
从上面可以看出,系统操作是用户的所有操作,即使这些操作并没有形成前端向后端的调用(这个时候还未考虑前后端交互问题)
对领域模型的修改
考虑下面一个契约
操作:enterSale()
交叉引用:用例:处理销售
前置条件:正在进行中的销售
后置条件:
- Sale.isComplete被置为真(修改属性)
上面的契约导致了领域模型新增了isComplete字段
个人的一点感悟,详尽的文档感觉是把一件事做了两遍,虽然这样保证你第二次做这件事的正确但是也导致了更大的工作量并且时间上也不允许
UP阶段
- 初始
初始阶段不会引入契约,因为过于详细.- 细化
…只对最复杂和微妙的系统操作编写契约
敏捷建模
一些敏捷建模的目标是减少常用图形,建模的目的是为了理解和沟通,而不是构建文档.
敏捷建模还包括
- 与其他人一同建模
- 并行创建若干模型
例如,在墙上用5分钟画交互图,然后再用5分钟画相关类图,反复交替
准则
应该把时间花费在交互图(顺序图或通信图),而不仅是类图上.
忽视这一准则是十分常见的UML错误实践
文中比较了动态建模和静态建模,对于我个人而言觉得动态建模更好,更贴近业务并且更容易关联理解
并且绘制动态图相对于绘制静态图的另一个优势是动态图不会出现和代码矛盾的情况,其可以作为代码的补充
CRC卡
人们对不同设计方法各有偏好除了因为熟悉该种方法外,更重要的是因为每个人有不同的认知方式.不要假设所有人都认为图形优于文本,反之亦然.
文中提到CRC卡应该仅仅是为了防御,我baidu了一下没看到任何资料
GRASP
对象职责
- 行为职责
- 自身执行一些行为,如创建对象或计算
- 初始化其他对象中的操作
- 控制和协调其他对象中的活动
- 认知职责
- 对私有封装数据的认知
- 对相关对象的认知
- 对其他够导出或计算的事物的认知
看看上面的列表,创建其他对象也可以是对象职责,未必只能创建自己
职责和方法并非同一事物,职责是一种抽象,而方法实现了职责
RDD是思考OO软件设计的一般性隐喻.把软件对象想象成具有某种职责的人,他要与其他人协作以完成工作.
模式
简单地讲,好的模式是成对的问题/解决方案,并且具有广为人知的名称,他能用于新的语境中,同时对新情况下的应用,权衡,实现,变化等给出了建议
术语模式的真实含义是长期重复的事物.设计模式的要点并不是要表达新的设计思想.恰恰相反,优秀模式的意图是将已有的经过验证的知识,惯用手法和原则汇总起来.磨砺的越多,越久,使用的越广泛,这样的模式就越优秀.
里程碑
全书到了这里,终于到了OO的起点:
- 迭代过程背景 --有哪些优先制品,他们与OO设计模型有什么关系?我们应当花费多少时间进行设计建模?
- 作为对象设计隐喻的RDD:有职责对象协作的共同体
- 作为OO设计思想命名和解释方式的模式:分配职责的基本模式是GRASP,对于更为高级的设计思想则应用GoF模式.模式可以在建模期间和编码期间应用
- UML用于OO设计的可视建模,在此期间,GRASP和GoF模式都能使用.
书中继续讲下去,但是我觉得在这里暂停一下是很有必有的,因为我们有太多的方案只是在解决问题而没有指出解决问题的环境.而对于从理论走向实践而复杂而言,这种设身处地的限制解决方案的应用环境是很有必要的,也显示出自己是一个懂行的实践者.