【关键字keyword】
uml接口interface是一个只有公共操作public operations没有方法体method body的类。他用类图标class icon和关键字<<interface>>表示。
<<interface>>简写为<<I>>,{abstract}简写为{A}。
uml1中引号guillemets主要用于原型stereotype。
原型被用作概况profile的一部分。
【分类和一般化classification and generalization】
语法:
Shep is a Border Collie
A Border Collie is a Dog
Dogs are Animals
A Border Collie is a Breed
Dog is a species
classification分类——object Shep是type Border Collie的一个实例
generalization一般化——type Border Collie是type Dog的子类型
一般化是可传递的transitive,而分类不可以。我可以组合一个分类,后面接一个一般化,但反过来不可以。
uml用一般化标识symbol来表示一般化。如果你需要显示分类,使用一个依赖dependency(使用关键字<<instantiate>>)。
【多重、动态分类Multiple and dynamic classification】
分类指一个对象object和它类型type间的关系。
在单类single classification中,一个对象属于一个单个类型(可能从父类型supertype继承)。在多类中,一个对象被几个类型描述(且不是必要的用继承来联系)。
多类和多继承不同。多继承指一个类型可能有多个父类型,但一单个类型必须为每个对象定义。多类允许有多个类型给一个对象,而不用为此定义一个特殊的类型。
见图5.11.
如果你使用多类,你需要确定哪些组合是合法的。uml2为此将哪些一般化的关系放到一个一般化集合去generalization set。在类图中,你用这个一般化集合的名字 标注这一般化的箭头,在uml1中它叫discriminator辨别物。单类对应一个单个无名的一般化集合。
一般化集合是默认解体的(disjoint):父类型的任何实例只能是该集合中某一个父类型的实例。
说明,图表中合法的子类型组合:
(female, patient, nurse); (male, physiotherapist); (female, patient); (female, doctor, surgeon).而(patient, doctor, nurse)是不合法的,因为它包含两个来自role一般化集合的类型。
动态类dynamic classification允许对象用子类型结构改变类change class within the subtyping structure,而静态类static不能。使用静态类,类型type和状态state被分开。而动态类结合了他们。
【关联类association class】
它允许你添加属性、操作和其他特性到关联association去。
见图5.12.
从图可看出一个人person可能参加很多会议meeting,我们需要掌握他有多清醒的信息keep information about how awake that person was。我们可添加属性attentiveness注意力到这个关联去。
见图5.13。
图中将attendance出勤变成了一个完整的类full class,注意多重性的移动。
关联类添加一个附加的限制extra constraint——在任两个参与的对象间的关联类 只能有一个实例there can be only one instance of the association class between any 2 participanting objects.
见图5.14.
在uml中只有后者合法,对于每个person和skill的组合,你只能有一个competency。而前者不允许一个company有超过一个role在一个contract里面。如果你需要允许这个,你需要将role变成一个完整的类。
实现关联类不明显。我建议实现一个关联类就像一个完整的类,提供获取信息的方法给关联类链接link的类。(My advice is to implement an association class as if it where a full class but to provide methods that get information to the classes linked by the association class)。所以对于5.12,我会看到person有下面的方法:
class Person
List getAttendances()
List getMeetings()
这样一个person的客户可掌握参加会议的人。如果想要详细,他们可自己获取Attendances。如果你这样做,要记得加强限制——对任何person和meeting的组合只能有一个attendance对象。你需要在任何创建attendance的方法里放一个check。
如果有临时的关系,我用关键字<<temporal>>来表示关联。
【模板类(参数化parameterized)】
这个概念在使用强类型语言的集合collections时非常有用。用它,你可为一个集合set定义行为(通过定义一个模板类Set):
class Set<T> {
void insert(T newElem);
void remove(T anElem);
之后就可以为更多特殊的元素制造类:Set<Employee> employeeSet; 见图5.17.
一个参数化类的使用,比如
Set<Employee> employeeSet,叫做衍生derivation。这可通过两种方式展示:
·<parameter-name::parameter-value>,如果只用一个参数可以省略参数名
·另一种方法加强了和模板之间的联系,并允许你重命名绑定的元素bound element。
见图5.18.
bind关键字是refinement关系上的一个原型stereotype。这暗示EmployeeSet会遵循Set的接口。你可以把EmployeeSet想成Se的一个子类型。
衍生跟子类型subtyping不同。你不允许添加特性到绑定元素去,它是完全被模板指定的。
【枚举enumerations】
枚举用来显示一个固定值的集合,没有任何属性除了他们有意义的值。他们是用enumeration关键字的类。
见图5.20
【活跃类active class】
一个活跃类有实例instances,他们各自执行、控制自己的控制线程。方法调用会在一个客户的线程或在一个活跃对象的线程里执行。一个好的例子是一个命令处理器command processor,从外部接受命令对象,然后在他自己的控制线程里执行命令。
见图5.21.
【可见性visibility】
任何类都有public和private元素,public元素可被其他任何类使用,private元素只能被它拥有owning的类使用。
uml中,你可用一个可见性的指示符标记任何属性和操作,而它的意义是依赖语言的。uml提供4种简写:+public -private ~package #protected。
大多数时间,我不绘制可视化标记。我只在需要高亮某些元素可见性上区别的时候才使用。
【消息message】
见图5.22
标准的uml没有显示消息调用message calls的任何信息,但有时会见到这样的。
上面添加箭头到关联两边。这些箭头被标记为从一个对象发送到另一个对象的消息。
【职责responsibility】
见图5.1
【静态操作和属性】
静态特性在类图中用下划线表示。见图5.2
【聚合和组合aggregation and composition】
聚合aggregation是部分的关系part-of relationship。就像一辆车有一个引擎和轮子作为其组成部分。
见图5.3
见图5.4
一个Point的实例,可能是一个多边形polygon的部分,或者一个circle的中心,但不能两个都是。
一般规则是,尽管一个类可以是很多其他类的组件component,但任何实例都必须只是一个拥有者owner的一个组件。类图显示潜在的拥有者的多类,但任何实例只有单个对象作为其拥有者。
“不共享”no sharing的规则是组合的关键。另一个假设是 如果你删除polygon,你需要自动的确保他拥有的points也都被删掉了。
【派生的属性derived properties】
派生的属性可基于其他值计算出来。比如date range,他的length是通过start和end计算出来的。所以可认为length是从另外两个值得来的derived。
你可用derivation来指出一个计算的值和以存储的值的区别。比如上面就表示start和end是存储的,而length是计算的。
这个是用/来标记的。虽然这是一个普遍的使用,但我不强烈,因为他太关心DateRange的内部了。
见图5.5
【接口和抽象类abstract class】
一个抽象类是不能直接被实例化的类。你可以实例化其子类。一般一个抽象类有一些抽象的操作operations。一个抽象的操作abstract operation没有实现implementation。只是单纯的声明,那样客户端就能绑定这个抽象类。
在uml中更普遍的方法来展示一个抽象类或操作,是将其名字用斜体。也可用标签:{abstract}。
接口是一个没有实现的类,它所有的特性都是抽象的。关键字是<<interface>>。
类和接口有两种关系:提供provide和需求require。
一个类提供一个接口如果他可以替换接口。比如一个类通过实现接口或实现接口的一个子类型。
一个类需要一个接口如果他需要该接口的一个实例来工作。这里是对于接口有一个依赖。
通过使用接口,我允许自己 之后如果需要可以很容易的改变的实现。
见图5.6
例子:
private List lineItems = new ArrayList();
lineItems被声明为List,且Order的其他地方都没有依赖ArrayList。很普遍的是在创建的时候使用其具体类,之后使用接口。
任何类是一个接口和一个实现的混合。因此严格讲,给一个父类使用上面的标记是不合法的,因为不是一个纯接口。
见图5.7
【只读read-only和冻结frozen】
{readOnly}
{frozen} 在一个对象的整个生命周期中不能改变,经常叫做immutable不易变的。
【引用对象reference和值对象value】
引用对象:比如Customer,区分对象需要用id。
值对象:比如Date,新的对象很频繁的创建和摧毁。如果想看他们是否相同,你不看id,而是看他们代表的值是否相同。这意味着你需要些相等测试的操作符。
值对象应该是不易变的immutable。你不能将1-Jan-04改成2-Jan-04。而应该创建一个新的2-Jan-04对象来用。
uml使用数据类型data type的概念。数据类型和值对象不一样,不会有id,值对象有id(但不在相等的时候使用)。
当和一个值对象关联的时候我使用组合composition。你也可在一个值类型上用关键字:<<value>>或<<struct>>
【限制的关联qualified association】
见图5.10