upzhai 2010-04-28
一.建模
建模包含两个问题,一个是怎么建?另一个是模是什么?
第一个问题,依赖方法论或者认识论,同样的事物在不同世界观的人看来会产生不同
的结果。简而言之,就是面向对象和面向过程两种不同的方法论导致不同的建模结果,面向过程希望你全盘考虑,面向对象希望你把事物通过抽象角度分解成小块,问题就变得简单。
例子:在30秒内尽可能多的说出筷子、勺子和盘子的相同点和不同点。
针对这个例子,会在不知不觉中,每一组相同点和不同点都来自一个抽象角度,例如从用途角度,相同点是都是餐具,不同点是功能不同,筷子用于夹菜,勺子用于舀东西,盘子用于盛东西;从使用角度,相同点都是用手,不同点是手的动作不同等等。从这个例子可以看出,抽象角度的不同决定了建模方向的不同。回到软件建模,当试图为现实建模时,首先要决定的是抽象角度,即建立这个模型的目的是什么,一旦抽象角度确定,剩下的就变得顺理成章。
不论在需求分析、系统分析还是系统设计上,面对问题域时,首先不要决定去通盘考虑,而是找出问题领域里包含的抽象角度。如果抽象角度都找全了,并且这些角度都分析清楚了,问题领域也就解决了。虽然这些抽象角度再思考的时候可能是互不关联的。
具体来说,做需求的时候,首要目标不是要弄清楚业务是如何一步一步完成的,而是要弄清楚有多少业务的参与者?每个参与者的目标是什么?参与者的目标就是抽象角度。与分析一个复杂的业务流程相比,单独分析参与者的一个个目的简单得多。
第二个问题,则依赖于确定了抽象角度下的场景模拟。
一旦决定了抽象角度,就确定了一个目标。然后,就是找出那些能满足这一目标的事物,有趣的是寻找这些事物的过程并不是面向对象,而是过程化的。这是因为要达到一个目标必须要有动作附在静态的事物上,并产生一定效果,这样,我们必须搞清楚谁发出了动作,作用于什么事物,产生了怎样的后果。显然这种描述是过程化的,但是与面向过程不同的是,描述这个过程化的场景并不是最终目的,而是为了找出场景中贡献于场景目标的那些事物,以及这些事物是如何贡献于这个场景的。也就是说场景模拟,帮助我们找出抽象的对象,而场景本身则是这些对象在一定条件下交互的一个特定结果。当条件发生变化时,场景就会发生变化,我们并不试图控制这个场景。
一个由抽象角度确定了的目标需要由静态事物加上特定条件下产生的一个特定的场景来完成,即静态的事物(物)+特定的条件(规则)+特定的动作(参与者的驱动)=特定的场景(事件)。所以,模就是“人”、“事”、“物”、“规则”。
重要的建模公式:
问题领域=(1到n)个抽象角度
抽象角度=问题领域边界之外的参与者的业务目标=业务用例
业务用例=(1到n)个特定场景
特定场景=静态的事物+特定的条件+特定的动作或者
特定的事=特定的事物+特定的规则+特定的人的行为
二.用例驱动
用例驱动是统一过程的重要概念,或者说整个软件生产过程就是用例驱动的。用例可以驱动的内容包括:
(1)逻辑视图
(2)进程视图
(3)部署视图
(4)实施视图
三.抽象层次
抽象层次在面向对象方法中极其重要。学会站在不同的抽象层次上考虑问题是建立好模型的基础。
首先,抽象层次越高,具体信息越少,但概括能力越强;反之,具体信息越丰富,结果越确定,但相应的概括能力越弱。从信息的表达能力来说,抽象层次越高表达能力越丰富,越容易理解。越是具体的表达,信息量越大,越接近人脑的处理极限,人们的理解能力越是下降。但如果抽象层次太高,信息量过少的话,实施起来就有新的困难——信息量不足。在使用uml时觉得选择用例的粒度是一件困难的事情,实际上本质是没有找准抽象层次。
抽象有两种方法,一种是自顶向下,一种自底向上。自顶向下适用于人们从头开始认识一个事物。例如,介绍汽车的工作原理时,从发动机、传送装置、变速器等较高层次的抽象概念来讲比较容易明白。如果降低一个层次,从发动机讲起,就会比较困惑。如果从力学原理和热学原理讲解,根本就弄不明白了。自底向上的方法适用于在实践中改进和提高认识。例如在实践中发现了发动机的问题,因而改进发动机的结构,甚至采用新的发动机原理,最终能够提升汽车的质量。
在软件开发过程中,主体上应当采用自顶向下的方法,用少量的概念覆盖系统需求,再逐步降低抽象层次,直到代码编写。同时应当辅以自底向上的方法,通过总结在较低抽象层次的实践经验来改进较高层次的概念以提升软件质量。如果在做设计或分析的时候总是觉得信息之间的关系太复杂,总是难以理清头绪,需要考虑一下是否不适当地采用了自底向上的分析方法。
四.视图
视图是uml建模中另一个重要概念。视图用于组织uml元素,表达出模型某一方面的含义。但在实际工作中有这样的困惑:应该在什么地方应用视图?应用哪一种视图?总共需要哪些视图?
现实中每一个事物都有很多种不同属性,每个属性(或者说方面)都属于这个事物并且仅能够表达这个事物的一个部分。人们认识事物也是一样,只有了解了很多个方面后才能对这个事物有真正的了解。例如一辆汽车,需要了解它的大小、质量、外观、性能、安全等才会决定是否购买。上述的每个属性都是汽车的一个视图,每个视图都向观察者展示了目标对象的一个方面。只有将必要的方面都用视图展示出来,观察者才会真正了解这个事物。
相对视图,还有一个重要概念:视角。视角是针对每一个视图而言,不同的视角展示了同样信息的不同认知角度。例如,外观是汽车属性的一部分,那外观是不是一个视图就足够了?不是的,同样是查看外观,有时候从前面看汽车的前脸长什么样,有时候从侧面看车身流水线什么样,有时候从后面看尾箱长什么样。每一个不同的观察角度都展示了整体信息的一部分,这个部分也满足了观察者的某一个审视要求。
一方面,从信息的展示角度来说,恰当的视角可以让观察者更容易抓住信息的本质;另一方面,从观察者的角度,观察者只会关心信息中他感兴趣的那一部分视角。因此,在展示的时候选择适当地视角并展示给适当地观察者非常重要。
在建模工作中,建模的目的是向相关人(干系人)展示将要生产的软件产品,一个软件产品也和汽车一样,有着很多不同的方面。只有把这些方面都描述清楚,用很多个不同的视图去展示软件这些不同的方面——静态、动态、结构性、逻辑性等,才能说建立了一个完整的模型。为了说明这些不同的方面,uml定义了用例图、对象图、类图、包图、活动图等不同的视图。这些视图从不同方面描述一个软件的结构和组成,所有这些视图的集合表达了一个软件的完整的含义。所以,建模最主要的工作就是为软件绘制那些表达软件含义的视图。
同时,由于软件的干系人很多,有客户、系统分析员、架构师、设计师、开发人员、测试人员、项目经理等,他们对同样信息的审视角度是不同的。即便是客户,普通业务员和经理要求的视角也不尽相同,例如针对同一个业务模块,经理更关心整体业务流程,业务员更关心表单填写。因此建模另一项重要的工作就是为不同的干系人展示他们所关心的那部分视角。
在实际的工作中,要经常思考两个问题:
(1)应该为哪些软件信息绘制哪些视图?
(2)应该给哪些干系人展示哪些视角?
五.对象分析方法
1.一切都是对象
在面向对象眼中,一切有名字的东西都是对象,都应当使用对象的观点来看待它、分析它,哪怕名字叫某某业务流程,仍然应当看作是一个对象。
2.对象都是独立的
对象与对象之间是天然独立的,只是在某个特定场景下,它们的某一个特定的实例才相互联系在一起。
我们获取和分析对象的手段经常是通过分析某个场景,但需要知道,对象是离散的,它不是因为该场景而存在,场景中的对象只是对象“映射”到该场景中的一个侧面,我们称之为对象实例。换言之,通过一个场景,我们仅能得到对象的一个侧面信息。
所以,要深入了解对象,经常需要分析很多个对象的实例所参与的场景,以获得对象的多个侧面,再通过归纳整理这些对象的多个实例抽象出来对象的一般性。这就是对象的分析方法。
对象是独立于场景的,不要将对象局限于场景中,对象的独立性带来的正是对象的可抽象能力和可扩展能力。
3.对象都具有原子性
无论什么时候,在分析过程中都应当将对象视为一个不可分割的原子,哪怕这个对象的规模很大。例如在分析一个商业过程时,对象的粒度大到如银行、商场的程度,但不论它有多巨大,只要我们认为它是对象,它与其他对象交互时就是一个整体,不能分割。
在分析过程中,对象总有一个边界,永远也不应该打破边界去窥视对象的内部。我们应当将分析过程中得到的所有对于对象的认识附加在对象边界上,在实现这个对象之前不理会其内部的细节。这称之为面向接口编程。
4.对象都是可抽象的
对象有着很多个不同的方面,一般来说,对象参与一个场景时会展现出某一个方面。总可以将对象的某一个方面抽象出来,让其作为对象的一个代表来参与场景交互。通常这种抽象会以接口来命名。在分析过程中,得到的任何一个对象都有特定的方面可以作为抽象,因为对象总是从场景分析中得到的,它在场景中肯定展现了一个方面。
对象所具有的方面,或者说对象所参与的场景越多,对象越有抽象价值,反之则越没有抽象价值。因此在分析过程中,应当关注于那些参与了很多场景的对象,它们往往是分析设计中的重点以及成败关键。
5.对象都有层次性
对象是有抽象层次的,层次越高,其描述越粗略但适应能力越广;层次越低,则描述越精确但适应能力越下降。在分析过程中,应当根据问题领域的复杂程度设定多个抽象层次,在每个抽象层次上使用适合的抽象程度的对象描述。这将有助于显著地减少分析的难度和工作量。