2005119@gmail.com
v1.0
摘要Q在对“靓L用”项目的重构中,我通过状态图很好的理解了业务逻辑。由此进一步归U了状态图在开发过E中的用提C和技巧?/SPAN>
关键词:UMLQ状态图Q重?/SPAN>
读者水qI初
“靓L用”是原无U技术部门开?/SPAN>GLSMRPIDService中的一个部分,׃q部分存在已的功能缺P需要对之进行适当的修改,通过q种方式来功能得到完善q版本得到演进。我先给出当前需求,对对象“靓号”的状态图?SPAN lang=EN-US>
按图说图图(1Q描qC对象“靓号”在其生命期中的几个状态,“靓号”能被锁定,被锁定的“靓号”不能被其它用户再锁定。被锁定的靓号如果在15分钟内没被用L用,则还回到初始状态,可以再被Q其他)用户锁定。被锁定的“靓号”可以被U用Q租用到期后能被pȝ预留Q即为先前的用户保护hQ,预留一个月后如用户未箋U,则此“靓号”可以被其他人锁定或U用。在U用状态下的“靓号”,如果被同一用户累积U用过半年Q则可以买断。被买断后,此“靓号”变成普通号?BR>
很遗憾,原设计由于所面对的问题领域规模小Q所以ƈ没有采用OOD/OOP的方式,所以看不到c?/B>
准则1: 状态图只对单一对象的复杂行行模建。这里的对象指类、角艌Ӏ子pȝ、或lg?/SPAN> |
因此Q状态图q不为多个对象之间的行ؓ建模。多个对象之间的行ؓ建模参考“活动图”,“时序图”,对象之间的关pd考“类䏀,“对象图”以及参考设计模?/SPAN>(设计模式通常?/SPAN>UML?/SPAN>Booch图表C类之间的关p?/SPAN>, Booch?/SPAN>UML的前w?/SPAN>)。现在我们给出状态图的定义?/SPAN>
状态图Q全UCؓ状态机视图(state machine view)Q通过Ҏ个类的生个对象Ş字的生命期徏模,描述了对象在旉上的动态行为?/SPAN>状态图用于Ҏ型元素的动态行行徏模,更具体地_是对系l行Z受事仉动的斚wq行建模?/SPAN>
状态图q态组成,各状态由转移链接在一赗状态是对象执行某项zd或等待某个事件时的条件。{UL两个状态之间的关系Q它由某个事件触发,然后执行特定的操作或评估q导致特定的l束状态。图 (2) 描绘了状态图的各U元素?/SPAN>
? |
态是对象执行某项zd或等待某个事件时的条件。对象可能会在有限的旉长度内保持某一状态。状态具有以下几特征:
名称 |
一个状态与其他状态区分开来的文本字符Ԍ状态也可能是匿名的Q这表示它没有名U?/SPAN> |
q入/退出操?/SPAN> |
在进入和退出状态时所执行的操作?/SPAN> |
内部转移 |
在不使状态发生变更的情况下进行的转移?/SPAN> |
子状?/SPAN> |
状态的嵌套l构Q包括不相连的(依次处于zd状态的Q或q行的(同时处于zd状态的Q子状态?/SPAN> |
延迟的事?/SPAN> |
未在该状态中处理但被延迟处理Q即列队{待由另一个状态中的对象来处理Q的一pd事g?/SPAN> |
如图 (2) 所C,可以为对象的状态图定义两种Ҏ的状态。初始状态指C状态图或子状态的默认起始位置?/SPAN>
在实际的目开发中Qƈ不是Ҏ一个类都画状态图。何旉要状态图Q我们可以采用下面的原则来确定:
敏捷建模( AM) ( Ambler 2002)的原则-Q最大化目q系人的投资Q-你只有当模型能够提供正面价值的时候才创徏模型?/SPAN> 如果一个实体,比如一个类或组Ӟ表示的行为的序和当前的状态无养I那么M?/SPAN>UML状态图可能是没有什么用处的。例如一?/SPAN>CLogFilecd很简单,表示了那些你会在系l中记录一操作的数据,因此一?/SPAN>UML状态图没有Q何相关之处。而“靓号”这cd象就l比较的复杂?/SPAN>
l 当给定一w择Ӟ要用状态图的可视语义,而不要写l的转移代码。例如,不要用几个信可发一个{U,然后使用详细代码来管理以不同的方式依赖于信号的控制流。应使用由单独的信号来触发的单独转移。在隐藏了附加行为的转移代码中,要避免用条仉辑?/SPAN>
l Ҏ在状态期间等待的事g或正在发生的事g来命名状态。记住,状态不是“时间点”;它是状态图{待某个事g发生的时间段。例如,?/SPAN>waitingForEnd”这一名称比?/SPAN>end”更好;?/SPAN>timingSomeActivity”比?/SPAN>timeout”更好。不要让状态的名称看v来象是操作名?/SPAN>
l 在一个状态图内唯一地命名所有状态和转移Q这便于进行源U别的调试?/SPAN>
l 谨慎使用状态变量;不要在创建新状态时使用它们。如果状态不多,很少带有或不带有依赖于状态的行ؓQƈ且很有或根本没有可能与包含状态图的封装体q行或独立的行ؓQ就可以使用状态变量。如果有复杂的、依赖于状态的潜在q行行ؓQ或者如果必d理的事g可能来自于包含状态图的封装体之外Q则应考虑使用构g装体?/SPAN>
l 如果单个图中的状态超q?/SPAN> 5 * 2 个,应考虑使用子状态。在q里可以应用我们的常识:在一个非常规则的模式中可以有十个状态,但如果两个状态之间具有四十个转移Q显然就需要重新考虑了。务必要使状态图易于理解?/SPAN>
l 使用触发事g的事件和/或在转移期间发生的事件ؓ转移命名。选择更加易于理解的名U?/SPAN>
l 当您看见一个选择ҎQ应考虑是否可以作选择的职责委托给另一个构Ӟ以便其作ؓ一l将不同的信h供给装体遵照执行(例如Q代替对消息->数据 > x 的选择Q,q考虑是否可以让发送方或另一中间主角来作出决定,然后通过在信号名UC明确昄该决定的方式发送信P例如Q用名?/SPAN> isFull ?/SPAN> isEmpty 的信P而不是以值命名信号ƈ查消息数据)?/SPAN>
l 为在选择点中回答的问题指定描q性的名称Q例如?/SPAN>isThereStillLife”或?/SPAN>isItTimeToComplain”?/SPAN>
l 在Q何给定的装体中Q尽量选择点名UC持唯一Q其原因与{UdU需保持唯一相同Q?/SPAN>
l 转移的代码段是否太长Q是否应使用函数来代替它们,是否常用代码段记录为函敎ͼ转移应该cM于高层的伪代码,q且应当遵@?/SPAN> C++ 函数相同或更严格的长度规则。例如,代码过 25 行的转移可被认ؓ是过ѝ?/SPAN>
l 应根据函数执行的操作来命名函数?/SPAN>
l 要特别注意进入和退出操作:在进行更改后忘记更改相应q入和退出操作的情况其Ҏ发生?/SPAN>
l 退出操作可用于提供安全性功能,例如Q从?/SPAN>heaterOn”状态中的退出操作将关闭加热器,在这里,操作被用来强制执行一个断a语句?/SPAN>
l 通常Q除非状态图是抽象的q且由包含元素的子cLq行改进Q否则子状态应包含两个或更多个状态?/SPAN>
l 应该用选择Ҏ代替操作或{UM的条仉辑。选择点容易被看到Q而代码中的条仉辑则是不可见的Q很Ҏ被忽略?/SPAN>
l 避免使用警戒条g?/SPAN>
n 如果事g触发了几个{U,无法控刉先对哪个警戒条g求倹{这会生无法预料的l果?/SPAN>
n 可能有多个警戒条件ؓ?/SPAN>True”,但随后只能有一个{UR所选择的\径是无法预料的?/SPAN>
n 警戒条g是不可见的;要“看见”它们的出现更是困难?/SPAN>
n 避免使用cM程囄状态图?/SPAN>
u q可能表C您试图对ƈ不实际存在的抽象概念q行建模Q例如:
u 使用一个封装体来对最适合于数据类的行行徏模,?/SPAN>
n 通过使用紧密耦合的数据类和封装体cLҎ据类建模Q例如,数据cȝ于向四周传递类型信息,但封装体cd含了应与数据cȝ兌的大部分数据Q?/SPAN>
u 状态图的这U错误用法可以通过以下故障现象来识别:
u 被发送给“自己”的消息Q主要是Z重复使用代码
u 几乎没有状态,但有很多选择?/SPAN>
u 在某些情况下没有循环的状态图。在程控制应用E序中,或者在试图控制一个事件序列时Q这L状态图是有效的Q如果它们在分析q程中出玎ͼ则表C状态图已退化ؓ程图?/SPAN>
n 当发现问题时Q应采取以下措施Q?/SPAN>
u 考虑封装体分解责更明确的小单元Q?/SPAN>
u 更多的行ؓ转移C有问题的装体相兌的数据类中?/SPAN>
u 更多的行ؓ转移到封装体cdC?/SPAN>
u 制作更有意义的信P以避免对数据的依赖?/SPAN>
l 避免"黑洞"状态?/SPAN>
n 黑洞状态是那种只有变换q来但没有Q何变换发出的状态,q种情况要么׃该状态是一个最l状态,要么是你已l错q了一个或多个变换变换?/SPAN>
l 避免"奇迹"状态?/SPAN>
n 奇迹状态是那种只有变换发出但没有Q何变换进来的状态,q种情况要么׃该状态是一个v点,要么是你已l错q了一个或多个变换变换?/SPAN>
?/SPAN>UML用户手册?/SPAN>
?/SPAN>Rational Unified Process?/SPAN>
其它|络资源