我们学习了UML的一些知识和内容,下面是对UML的一些简单总结。
四种关系
我们大家已经学习过了UML(unified moduling language)统一建模语言,UML中核心是九种图,这九种图的核心就是四种关系,关联(association)、依赖(dependency)、继承(generalization)、实现(realization)这四种关系,他们表示的图符为:
1 .Association(关联):关联关系是一种结构化的关系,指一种对象和另一种对象有联系。给定关联的两个类,可以从其中一个类的对象访问到另一个类的相关对象。
依赖关系比较广,比较泛泛,一般两个对象之间经常会有关联关系,关联关系对对象之间关系的阐述并不具体,在工程画图中,一般要求准备表明两个对象之间的关系,而不能都用关联关系来表示,比如:苹果树和苹果,它们就是一对有关联关系的对象,但他们也有实现(realization)关系,这就需要我们准备写明对象之间的关系,这对项目结构设计很重要。
如果觉的两个业务概念之间有联系,但又不确定怎么样的关系,那就先画一条先联系起来,表明关联,随着你对业务的加深和具体化,再对关联关系进行完善和修改。
2.Dependency(依赖):对于两个对象X/Y,如果对象X发生变化,可能会引起对另一个对象Y的变化,则称Y依赖于X。
例如:一个烟鬼,嗜烟如命没有烟,不能生存下去,则他对烟可以说是依赖关系。表示如下:
所谓的依赖关系,依赖的程度是相当而言的,不一定A没有B就不能生存,在具体的业务逻辑中,对于某个事情,A需要B来协助完成,这也是一种依赖关系。
3.Generalization(泛化):UML中的泛化关系定义了一般元素和特殊元素之间的分类关系,与和C++及java中的继承关系有些类似。
例如:三个类student 、teacher、 employee,我们可以知道,学生和老师其实都是员工,它们都继承了employee的特性,也都有自己的属性,可以表示为:
上如,表明student和teacher继承了employee的特性,箭头指的被继承的类。
4.Realization(实现):实现关系将一种模型元素(如类)与另一种模型元素(如接口)连接起来,其中接口只是行为的说明而不是结构或者实现。真正的实现由前一个模型元素来完成。keyboard类实现,typewriter接口的功能。
在UML中最基本的是这四种关系,有的关系还可以细化,首先,把这些关系弄清楚了才能够画UML中的九种图,因为它们都是依赖与这四种关系才组成的。
1、继承是面向对象编程一大特点,如果两个对象A和B,可以描述为“B是A”,则表明B可以继承A。继承者还可以理解为是对被继承者的特殊化,因为它除了继承者的特性外,还具备自己独有的特性;继承一般可说成父类和子类或基类和派生类。如下面例子:
a 为了使子类的实例完全接替来自父类的成员,父类必须将该成员声明为虚拟的。在返回类型之前加上关键字“virtual”。
b 子类可以选择使用关键字“override”,将父类实现替换为它自己的实现。
c 这样方法重写,其实,是实现了“多态”。
d 多态是指不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行。
继承的优点是使得代码到了共享,避免了重复,增加了功能的可扩展性,而缺点为父类变,则子类就变,父类实现细节暴露给子类,增强了类与类之间的耦合性。
2、依赖是一种类与类之间弱耦合的关系,如下图用虚线箭头表示:
在五种关系中,依赖是耦合最小的一种,在类与类的代码中都不会增加属性,如下图:
a 在Animal类中,没有Water属性,那么Animal类怎么使用Water类呢?有三种方式
1 Water类是全局的,则Animal类可以调用它
2 Water类是Animal类的某个方法中的变量,则Animal类可以调用它。
Water类的生命期,它是当Animal类的方法被调用的时候,才被实例化。
持有Water类的是Animal的一个方法而不是Animal类
3 Water类是作为Animal类中某个方法的参数或者返回值时。
Water类被Animal类的一个方法持有。生命期随着方法的执行结束而结束。
3 关联用实线箭头表示,如下图:
如下代码:在类cloud属性中,添加了Climate
关联既有单向关联又有双向关联,上图中为单向关联,Cloud类成为源类,Climate类成为目标类,源类了解目标类的属性、成员、信息,但目标类不了解源类。
双向关联,有双向箭头,信息相互了解。
在各自代码中,都有对方类,作为自己的一个属性成员。
依赖和关联的区别:
a 从类的属性是否增加的角度看:
发生依赖关系的两个类都不会增加属性。其中的一个类作为另一个类的方法的参数或者返回值,或者是某个方法的变量而已。
发生关联关系的两个类,其中的一个类成为另一个类的属性,而属性是一种更为紧密的耦合,更为长久的持有关系。
b 从关系的生命期角度看:
依赖关系是仅当类的方法被调用时而产生,伴随着方法的结束而结束了。
关联关系是当类实例化的时候即产生,当类销毁的时候,关系结束。相比依赖讲,关联关系的生存期更长。
l 聚合和组合的区别:
我们大家都知道表面上他们的区别,聚合比组合关系弱一些,那么这个弱的关系如何在代码中体现呢?
区分的关键有两点:
下面为,大话设计中一个例子:两个类之间为聚合关系。
<span style="font-size:24px;"><span style="color:#009900;"> //工作经历类 class WorkExperience { private string workDate; public string WorkDate { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } }</span><span style="color:#ff0000;"> //简历代码 class Resume:ICloneable { private string name; private string sex; private string age; //引用工作经历对象 private WorkExperience work; </span><span style="color:#009900;"> public Resume(string name) { this.name = name; //在“简历”类实例化时,同时实例化,“工作经历类” work = new WorkExperience(); }</span><span style="color:#ff0000;"> //设置个人信息 public void SetPersonInfo(string sex, string age) { this.sex=sex; this.age=age; } //设置工作经历 public void SetWorkExperience(string workDate,string company ) { //在调用此方法时,给对象的两个属性赋值 work.WorkDate = workDate; work.Company = company; } public void Display() { Console.WriteLine("{0}{1}{2}",name,sex,age); //显示时,显示“工作经历”的两个属性值 Console.WriteLine("工作经历:{0}{1}",work.WorkDate,work.Company); } public object Clone() { return (object)this.MemberwiseClone(); } }</span></span> |
这两种关系的区别在于:
①构造函数不同
聚合类的构造函数中包含了另一个类作为参数。
雁群类(GooseGroup)的构造函数中要用到大雁(Goose)作为参数传递进来。大雁类(Goose)可以脱离雁群类而独立存在。
组合类的构造函数中包含了另一个类的实例化。
表明大雁类在实例化之前,一定要先实例化翅膀类(Wings),这两个类紧密的耦合在一起,同生共灭。翅膀类(Wings)是不可以脱离大雁类(Goose)而独立存在
②信息的封装性不同
在聚合关系中,客户端可以同时了解雁群类和大雁类,因为他们都是独立的
而在组合关系中,客户端只认识大雁类,根本就不知道翅膀类的存在,因为翅膀类被严密的封装在大雁类中。
在这里涉及到一个设计模式即CARP模式,合成、聚合复用原则,尽量使用合成、聚合,尽量不用使用类继承。继承是一种强耦合关系,父类变,子类就必须变。
实战机房收费系统
我们已经学习完UML了,大家是否还记得学习UML的目的呢?
1.进一步规范、明确的指导我们代码的实现。
2.实现团队合作的基础。
只有关系明确,分工明确才有可能多人合作,进行团队作业。
学习好用UML画图会对以后写代码、团队合作打下良好的基础,因此,我们要学会利用rational
rose画图。
下面是,对机房收费系统简单的画图。
以上图并不完整,继续整理……
为什么要用UML建模之建模的重要性
如果想搭一个狗窝,备好木料、钉子和一些基本工具(如锤子、锯和卷尺)之后,就可以开始工作了。从制定一点初步计划到完成一个满足适当功能的狗窝,可能不用别人帮助,在几个小时内就能够实现。只要狗窝够大且不太漏水,狗就可以安居。如果未能达到希望的效果,返工总是可以的,无非是让狗受点委屈。
如果想为家庭建造一所房子,备好木料、钉子和一些基本工具之后,也能开始工作,但这将需要较长的时间,并且家庭对于房子的需求肯定比狗对于狗窝的需求要多。在这种情况下,除非曾经多次建造过房子,否则就需要事先制定出一些详细的计划,再开始动工,才能够成功。至少应该绘制一些表明房子是什么样子的简图。如果想建造一所能满足家庭的需要并符合当地建筑规范的合格房屋,就需要画一些建筑图,以便能想清楚房间的使用目的以及照明、取暖和水管装置的实际细节问题。做出这些计划后,就能对这项工作所需的时间和物料做出合理的估计。尽管自己也可能建造出这样的房屋,但若有其他人协作,并将工程中的许多关键部分转包出去或购买预制的材料,效率就会高得多。只要按计划行事,不超出时间和财务的预算,家庭多半会对这新房感到满意。如果不制定计划,新房就不会完全令人满意。因此,最好在早期就制定计划,并谨慎地处理好所发生的变化。
如果你要建造一座高层办公大厦,若还是先备好木料、钉子和一些基本工具就开始工作,那将是非常愚蠢的。因为你所使用的资金可能是别人的,他们会对建筑物的规模、形状和风格做出要求。同时,他们经常会改变想法,甚至是在工程已经开工之后。由于失败的代价太高了,因此必须要做详尽的计划。负责建筑物设计和施工的是一个庞大的组织机构,你只是其中的一部分。这个组织将需要各种各样的设计图和模型,以供各方相互沟通。只要得到了合适的人员和工具,并对把建筑概念转换为实际建筑的过程进行积极的管理,将会建成这座满足使用要求的大厦。如果想继续从事建筑工作,那么一定要在使用要求和实际的建筑技术之间做好平衡,并且处理好建筑团队成员们的休息问题,既不能把他们置于风险之中,也不能驱使他们过分辛苦地工作以至于精疲力尽。
奇怪的是,很多软件开发组织开始想建造一座大厦式的软件,而在动手处理时却好像他们正在仓促地造一个狗窝。
有时你是幸运的。如果在恰当的时间有足够的合适人员,并且其他一切事情都很如意,你的团队有可能(仅是可能)推出一个令用户眼花缭乱的软件产品。然而,一般的情况下,不可能所有人员都合适(合适的人员经常供不应求),时间并不总是恰当的(昨天总是更好),其他的事情也并不尽如人意(常常由不得自己)。现在对软件开发的要求正在日益增加,而开发团队却还是经常单纯地依靠他们唯一真正知道如何做好的一件事——编写程序代码。英雄式的编程工作成为这一行业的传奇,人们似乎经常认为更努力地工作是面对开发中出现的各种危机的正常反应。然而,这未必能产生正确的程序代码,而且一些项目是非常巨大的,无论怎样延长工作时间,也不足以完成所需的工作。
如果真正想建造一个相当于房子或大厦类的软件系统,问题可不是仅仅编写许多软件。事实上,关键是要编出正确的软件,并考虑如何少写软件。要生产合格的软件就要有一套关于体系结构、过程和工具的规范。即使如此,很多项目开始看起来像狗窝,但随后发展得像大厦,原因很简单,它们是自己成就的牺牲品。如果对体系结构、过程或工具的规范没有作任何考虑,总有一天狗窝会膨胀成大厦,并会由于其自身的重量而倒塌。狗窝的倒塌可能使你的狗恼怒;同理,不成功的大厦则将对大厦的租户造成严重的影响。
不成功的软件项目失败的原因各不相同,而所有成功的项目在很多方面都是相似的。成功的软件组织有很多成功的因素,其中共同的一点就是对建模的采用。
建模是一项经过检验并被广为接受的工程技术。建立房屋和大厦的建筑模型,能帮助用户得到实际建筑物的印象,甚至可以建立数学模型来分析大风或地震对建筑物造成的影响。
建模不只适用于建筑业。如果不首先构造模型(从计算机模型到物理风洞模型,再到与实物大小一样的原型),就装配新型的飞机或汽车,那简直是难以想像的。新型的电气设备(从微处理器到电话交换系统)需要一定程度的建模,以便更好地理解系统并与他人交流思想。在电影业,情节串联板是产品的核心,这也是建模的一种形式。在社会学、经济学和商业管理领域也需要建模,以证实人们??的理论或用最小限度的风险和代价试验新的理论。
那么,模型是什么?简单地说:
模型是对现实的简化。
模型提供了系统的蓝图。模型既可以包括详细的计划,也可以包括从很高的层次考虑系统的总体计划。一个好的模型包括那些有广泛影响的主要元素,而忽略那些与给定的抽象水平不相关的次要元素。每个系统都可以从不同的方面用不同的模型来描述,因而每个模型都是一个在语义上闭合的系统抽象。模型可以是结构性的,强调系统的组织。它也可以是行为性的,强调系统的动态方面。
为什么要建模?一个基本理由是:
建模是为了能够更好地理解正在开发的系统。
通过建模,要达到4个目的:
(1)模型有助于按照实际情况或按照所需要的样式对系统进行可视化。
(2)模型能够规约系统的结构或行为。
(3)模型给出了指导构造系统的模板。
(4)模型对做出的决策进行文档化。
建模并不只是针对大的系统。甚至像狗窝那样的软件也能从一些建模中受益。然而,可以明确地讲,系统越大、越复杂,建模的重要性就越大,一个很简单的原因是:
因为不能完整地理解一个复杂的系统,所以要对它建模。
人对复杂问题的理解能力是有限的。通过建模,缩小所研究问题的范围,一次只着重研究它的一个方面,这就是Edsger
Dijkstra几年前讲的“分而治之”的基本方法,即把一个困难问题划分成一系列能够解决的小问题;解决了这些小问题也就解决了这个难题。此外,通过建模可以增强人的智力。一个适当选择的模型可以使建模人员在较高的抽象层次上工作。
任何情况下都应该建模的说法并没有落到实处。事实上,一些研究指出,大多数软件组织没有做正规的建模,即使做了也很少。按项目的复杂性划分一下建模的使用情况,将会发现:项目越简单,采用正规建模的就越少。
这里强调的是“正规”这个词。实际上,开发者甚至对非常简单的项目也要做一些建模工作,虽然很不正规。开发者可能在一块黑板上或一小片纸上勾画出他的想法,以对部分系统进行可视化表示,或者开发组可能使用CRC卡片描述一个场景或某种机制的设计。使用任何一种这样的模型都没有什么错。如果它能行得通,就可以使用。然而,这些非正规的模型经常是太随意了,它没有提供一种容易让他人理解的共同语言。建筑业、电机工程业和数学建模都有通用的建模语言,在软件开发中使用一种共同的建模语言进行软件建模也能使开发组织获益匪浅。
每个项目都能从一些建模中受益。即使在一次性的软件开发中——由于可视化编程语言的支持,可以轻而易举地扔掉不适合的软件。建模也能帮助开发组更好地对系统计划进行可视化,并帮助他们正确地进行构造,使开发工作进展得更快。如果根本不建模,项目越复杂,就越有可能失败或者构造出错误的东西。所有实用系统都有一个自然趋势:随着时间的推移变得越来越复杂。虽然今天可能认为不需要建模,但随着系统的演化,终将会对这个决定感到后悔,但那时为时已晚。
如何提取用例技术?
这次做机房系统已经提取过一遍用例图了,但是选取用例不够准备,虽然这次没有什么影响,还是需要学会如何提取用例,这是做系统的第一步。
提取用例的书和资料很多,讲的都很类似,主要是从角色和系统功能两个角度去考虑,对于我们没有开发经验的初学者来说,
可能面临的问题有两个:一是我们知道了系统功能后,应该怎么确定用例;二是提取用例多少的问题,即粒度问题。如果没有丰富的经验可能会把握不好用例的提取。
想确定用例,我们必须知道用例的定义,即什么是用例:用例试图概括了用例中角色和系统之间的关系,描述了系统功能需求,角色和系统的交互以及系统的反映。如下面例子:
用例的粒度问题,也即使不用为Include 和Extend所困扰,如果Include
、extent使用频繁则粒度细,反之,粒度粗。
include 包含是主用例没有包含其他的辅助用例就不能独立执行,但是主用例可以在没有“扩展”其他辅助用例的情况也可以执行。比如说注册前需要检查验证码,但是不一定会“扩展”发送通知邮件这个用例。
extent表示一个用例有可能扩展到另外一个用例的功能,用例中的扩展通常暗示一个选择性流程。
有些公司甚至为了使用例简化,取消了Include 和extent ,这是极端的做饭,显然,不利于用例提取。
合理把握粒度,需注意以下问题:
1.做用例前,要先弄清楚研究对象是什么,并时刻提醒自己不要偏离主题。不然会发生“患者到医院挂号”,或者“患者到医院信息系统看病”之类的笑话。
2.只要在形式上能写出符合需求标准的路径、步骤,都可以作为用例。注意,是“可以”,并不是“一定”。
3.做用例分析时最常犯的错误是:把步骤当作用例。如“取款”用例中的“验证密码”与“扣除帐户金额”,它们是“取款”用例的步骤,而不是其子用例。
4.include的目的是为了复用有价值的步骤集合。形式往往是多个大用例include一个可复用的用例,即“多个老大include一个小弟”。
例如:
5.用例是否用对了的一个判断标准是:其是否加强了和涉众的联系。如多级审批中,局长乐意跟科长共享一个审批功能吗?
,又如下面图:
6.层次问题的出现常常是因为把研究对象弄错了,或者将系统契约与非契约混在一起。如将医院的职责“强加”给了医院信息系统。
7.讲究“复用”不是需求要考虑的事情,而是设计要考虑的。高焕堂老师说:需求是收益面,设计是成本面。
8.只有正确提取用例,以下的工作才能顺利进行,通过看这篇博客,希望大家学会提取用例!
|