3.8 设计类

只供参考,喜欢请支持正版图书

设计类是系统实施中一个或多个对象的抽象;设计类所对应的对象取决于实施语言。设计类用于设计模型中,它直接使用与编程语言相同的语言来描述。

凡是使用过面向对象语言的朋友对类都不会陌生,到了这个阶段,设计类已经直接映射到实现代码了,因此设计类依赖于实施语言。另一方面,设计类来源于前期的系统分析,在统一过程中,类不是凭空想象出来的,它们可以一一映射到前期系统分析的成果物上。从这个观点出发,分析类的重要性就能够体现出来。分析类为设计类中所需要的界面、逻辑和数据提供了非常好的抽象基础,设计类可以非常容易和自然地从分析类中演化出来,关于这一点,在5.8设计模型一节中将进行讨论。

3.8.1 类
类对对象进行定义,而对象又实现(或称为实施)用例。类的来源可以是用例实现对系统所需对象的需求,这是为实现业务需求而定义的;也可以是任何以前已开发的对象模型,即现有的系统模块、采用的软件框架、第三方产品等。类说明了对象是什么,同时也就决定了对象拥有什么属性,具有什么方法。在Java和C++这些典型的面向对象语言里,类就对应于一个class声明。

3.8.2 属性
属性是对象特征,属性同时表明了对象的唯一性

3.8.3 方法
原则上,访问对象或影响其他对象的属性或关系的唯一途径就是方法,直接访问和修改对象的属性是不提倡的

3.8.4 可见性
■ 公有:除了类本身以外,属性和方法对其他模型元素也是可视的。公有可见性应该尽量少用,公有意味着将类的属性和方法暴露给外部,这与面向对象的封装原则是矛盾的。暴露给外部的内容越多,对象越容易受影响,越容易形成高耦合度。

■ 保护:属性和方法只对类本身、它的子类或友元(取决于具体语言)是可视的。保护可见性是默认的可见性;它保护属性和方法使其不被外部类使用,防止行为的耦合和封装变得松散。

■ 私有:属性和方法只对类本身和类的友元(取决于具体语言)是可视的。私有可见性可以用在不希望子类继承属性和方法的情况下。它提供了从超类对子类去耦的方法,并且减少了删除或排除未使用继承操作的需要。

■ 实施:属性和方法只在类本身的内部是可视的(取决于具体语言)。实施可见性最具限制性;当只有类本身才可以使用操作时,使用这种可见性。实施可见性是私有可见性的变体。

3.9 关系

3.9.1 关联关系(association)

关联关系是用一条直线表示的,它描述不同类的对象之间的结构关系,它在一段时间内将多个类的实例连接在一起。关联关系是一种静态关系,通常与运行状态无关,而是由“常识”、“规则”、“法律”等因素决定的,所以关联关系是一种“强关联”的关系。

例如,公司与员工之间一对多就是一种符合“常识”的关系;乘车人和车票之间的一对一关系是符合“规则”的关系;公民和身份证之间的一对一关系是符合“法律”的关系。

关联关系用来定义对象之间静态的、天然的结构,关联的两个对象之间通常不会相互直接使用,尽管它们相互“知道”对方的存在,但一般都是由外部对象来访问的,如一个外部访问者可以通过员工对象获得公司对象

关联关系具有多重性,常见为一对一关联、一对多关联、多对多关联等,也可以是任意多重性关联,如关联(*代表任意数)

关联关系一般不强调关联的方向,当A——B时,我们默认为A和B都相互“知道”对方的存在。大多数情况下,这也是适当的。但如果特别强调了关联的方向,如A——>B,那么表示的是A“知道”B,但B不知道A。

特别的,在用例模型中,单向关联关系用于连接参与者和用例,箭头由参与者指向用例,表示参与者“知道”用例的存在。当然,在有些UML建模工具中,方向是不被强调的,不过我们从用例知识中了解到,总是参与者“知道”用例,而用例是“不知道”参与者的。

3.9.2 依赖关系(dependency)

依赖关系是用一条带箭头的虚线表示的,它描述一个对象在运行期会使用到另一个对象的关系。与关联关系不同的是,依赖关系是一种临时性的关系,它通常都是在运行期产生,并且随着运行场景的不同,依赖关系也可能发生变化。

例如人和船这两个对象,如果运行场景是开动轮船,那么轮船依赖于人(水手);如果场景变为渡海,那就变成人依赖于船了。可见,依赖关系是一种“弱”关系,它不是天然存在的,并且会随着运行场景的变化而变化。如人和刀这两个对象,平时它们是没有关系的,但在削苹果这个场景里,人依赖于刀;脱离了这个场景,或者说当场景结束后,依赖关系也就不存在了。

一般而言,依赖关系在最终的代码里体现为类构造方法、类方法等的传入参数。与关联关系相比,依赖关系除了临时“知道”对方外,还会“使用”对方的属性或方法。从这个角度讲,被依赖的对象改变会导致依赖对象的修改。举例来说,A对象保存了B对象的实例,但A对象对B对象没有操作,这时A仅仅是“知道”B对象,应当用关联关系,并且B修改了方法以后,A并不会变化;但如果A对象在某个场景当中使用了B对象的属性或方法,则B的修改会导致A的修改,这时A依赖于B。

同样的,依赖也有单向依赖和双向依赖之分。但是依赖关系却不像关联关系那样有带箭头和不带箭头的区分,统统都是带箭头的。这是因为在面向对象里,双向依赖是一种非常不好的结构,我们总是应当保持单向依赖,杜绝双向依赖关系的产生。

3.9.3 扩展关系(extends)

扩展关系是用一条带箭头的虚线加版型<>来表示的,A扩展出B,它特别用于在用例模型中说明向基本用例中的某个扩展点插入扩展用例。

一般来说,扩展用例是带有抽象性质的,它表示了用例场景中的某个“支流”,由特定的扩展点触发而被启动。所以严格来说扩展用例应当用在概念用例模型中,通过分析业务用例场景抽象出关键的可选核心业务而形成扩展用例。不过,在业务模型当中使用也是可以接受的,它可以更显式地表示出一个复杂业务用例的各个“分支”。

与包含关系不同的是,扩展表示的是“可选”,而不是“必需”,这意味着即使没有扩展用例,基本用例也是完整的;如果没有基本用例,扩展用例是不能单独存在的;如果有多个扩展用例,同一时间用例实例也只会使用其中的一个。

在建模过程中,我们使用扩展关系可能基于以下理由:
■ 表明用例的某一部分是可选(或可能可选)的系统行为。这样就可以将模型中的可选行为和必选行为分开。

■ 表明只在特定条件(有时是例外条件)下才执行分支流,如触发警报。

■ 表明可能有一组行为段,其中的一个或多个段可以在基本用例的扩展点处插入。所插入的行为段(以及插入的顺序)将取决于在执行基本用例时与主角进行的交互。

■ 表明多个基本用例中都有可能触发一个可选的分支流。从这个意义上说,扩展用例也代表了多个用例的可复用部分

为了理解扩展关系,让我们来看一个例子。在打电话时,如果在通话过程中收到另一个呼叫,我们可以将当前通话保留而接听另一个通话。在这个场景中,保留通话用例就是打电话用例的一个扩展用例。我们可以看到,是否需要保留通话取决于打电话人的决定,而不是必需,即使我们没有使用保留通话功能,也不影响打电话的完整性。但是如果没有之前的打电话用例,也就不可能单独启动所谓的保留通话用例了。

3.9.4 包含关系(include)
包含用例总是带有抽象性质的,基本用例可控制与包含用例的关系,并可依赖于执行包含用例所得的结果,但基本用例和包含用例都不能访问对方的属性。从这种意义上讲,包含用例是被封装的,它代表可在各种不同基本用例中复用的行为。因此,与扩展用例一样,包含用例也应当用在概念用例模型中,通过分析业务用例场景而抽象出关键的必选的核心业务而形成包含用例。同样,在业务模型中使用也是可以接受的,它可以显式地表示出那些可复用的业务过程

与扩展用例不同的是,包含用例表示的是“必需”而不是“可选”,这意味着如果没有包含用例,基本用例是不完整的,同时如果没有基本用例,包含用例是不能单独存在的。

在建模过程中使用包含关系可能基于以下理由:

■ 从基本用例中分解出这样的行为:它对于了解基本用例的主要目的并不是必需的,只有它的结果才比较重要。

■ 分解出两个或更多个用例所共有的行为。

为了理解包含关系,让我们来看一个例子。去银行办理业务,不论是取钱、转账还是修改密码,我们都需要首先核对账号和密码,因此可以将核对账号作为上述业务用例的共有行为提取出来,形成一个包含用例。我们可以看到这个包含用例就带有了可复用的意义,如果缺少了包含用例,取钱、转账等业务用例是不完整的,同时,核对账号也不能脱离开取钱、转账等业务用例而单独存在。

3.9.5 实现关系(realize)

实现关系是用一条带空心箭头的虚线表示的,它特别用于在用例模型中连接用例和用例实现,说明基本用例的一个实现方式。

实现所代表的含义是,基本用例描述了一个业务目标,但是该业务目标有多种可能的实现途径,每一种实现途径可以用用例实现(或称用例实例)来表示,而用例实现与基本用例之间就构成了实现关系。换言之,每个实现途径都实现了基本用例的业务目标。

我们用如图3.28所示的交纳电话费业务作为例子,可以看到,交纳电话费是一个业务目标,其实现途径可能有营业厅交费、银行交费、预存话费等,每一个用例实现都是同一业务目标的不同实现过程,因此它们之间是实现的关系。
3.9.6 精化关系(refine)
精化关系是用一条带箭头的虚线加版型<>来表示的,
它特别用于用例模型,一个基本用例可以分解出许多更小的关键精化用例,这些更小的精化用例更细致地展示了基本用例的核心业务。精化关系用来连接基本用例和精化用例,说明精化用例是由基本用例精化得来的。

精化关系也可以用于模型与模型之间,表示某个模型是通过精化另一个模型而得来的。比如说,我们认为设计类是通过精化分析类而得来的,我们可以用XX设计类<>XX分析类来表示它们之间的关系。

与泛化关系不同的是,精化关系表示由基本对象可以分解为更明确、精细的子对象,这些子对象并没有增加、减少、改变基本对象的行为和属性,仅仅是更加细致和明确化了。在泛化关系中,基本对象被泛化成为子对象后,子对象继承了基本对象的所有特征,并且子对象可以增加、改变基本对象的行为和属性。

精化关系仅仅用于建模阶段,在实现语言中是没有精化这一语义的

从业务模型中分析出实现业务目标的那些核心行为和实体,从而描述出一个关键的业务结构以得到一个易于理解的业务框架。这些关键概念就是对业务用例的精化。它们表示为概念用例到业务用例的精化关系
作为例子,图3.29展示了预存话费业务用例被精化成了四个核心的概念用例,这些概念用例合在一起就满足了实现业务目标的所有关键过程。我们可以根据这些精化结果建立业务框架。

3.9.7 泛化关系(generalization)

泛化关系是用一条带空心箭头的直线表示的,。泛化关系可用于建模过程中的任意一个阶段,说明两个对象之间的继承关系。读者对面向对象中的继承应该不会陌生,泛化关系表示一个类对另一个类的继承。继承而得的类称为后代。被继承的类称为祖先。继承意味着祖先的定义(包括任何特征,如属性、关系或对其对象执行的操作)对于后代的对象也是有效的。泛化关系是从后代类到其祖先类的关系。

3.9.8 聚合关系(aggregation)
与组合关系不同的是,整体和部分不是强依赖的,即使整体不存在了,部分仍然存在。例如部门撤销以后,人员不会因此而消失,他们依然存在。

3.9.9 组合关系(composition)

组合关系是用一条带实心菱形箭头的直线表示的,A组合成B,或者说B由A构成。需要特别说明的是

组合关系用于类图,特别用于表示实体对象关系,表达整体拥有部分的语义。例如母公司拥有许多子公司。
组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也将消亡。例如母公司解体了,子公司也将不再存在。

3.10 组件

组件是系统中实际存在的可更换部分,它实现特定的功能,符合一套接口标准并实现一组接口。组件代表系统中的一部分物理实施,包括软件代码(源代码、二进制代码或可执行代码)或其等价物(如脚本或命令文件)。

建模过程中,我们通过组件这一元素对分析设计过程中的类、接口等进行逻辑分类,一个组件表达软件的一组功能。例如一个网站有用户注册和用户维护两个目标功能,通过对网站需求的用例分析和设计,我们得到许多类和接口,这些类和接口实现网站的用户管理。出于构件化的需要,我们把那些紧密合作的类和接口组合起来实现一组特定的功能,形成一个组件。

一个类可能被分派给多个组件以完成该组件的功能,当组件被编译或打包成一个物理文件时,每个组件都拥有这个类的一个拷贝或者引用该类的途径。

一个组件应当具有完备性、独立性、逻辑性和透明性。这些性质在接下来的内容里会详细讲述。但是这样一来,就引出了另一个问题,组件之间如果是独立的,它们之间的关系又是什么呢?因此,基于组件的不同理解,笔者对组件之间关系的理解也与UML有所不同。

在UML的定义中,组件之间唯一的关系就是依赖,在Rose中,组件视图中允许的唯一连接也是依赖关系,而依赖意味着一个组件的修改会导致依赖于它的其他组件的修改

但是在笔者看来,一个组件应当是一个独立的业务模块,有着完备的功能,可独立部署,一个组件可以看成是一个完备的服务。从SOA架构的观点来看,一个SOA服务与其他服务是没有依赖关系的,服务与服务之间仅仅保持着松耦合的通信关系。如果组件之间有着依赖关系,那么定义组件就没有什么实用意义了,因为组件不能够独立存在。所谓构件化开发就是像搭积木一样建设系统,很难想象积木块之间有着千丝万缕的依赖关系还能够“自由”地搭建系统,所以笔者对UML关于组件关系的定义是心存疑问的。

笔者觉得,组件之间仅仅应当保持关联关系,甚至连关联关系都没有。它们之间是通过架构来沟通的,即松耦合,在发出消息之前,组件之间甚至不知道对方的存在

下面来看看组件的这些特性。

3.10.1 完备性
完备性是说,组件包含一些类和接口,一个组件应当能够完成一项或一组特定的业务目标(或说功能)。从调用者的观点看,它不需要调用多个组件来完成一个业务请求。

例如我们将组件A定义为用户注册,那么我们应该在组件A中包含所有实现用户注册的必需的类和接口,在任何时候,仅通过组件A就可以注册一个用户而无须访问组件外的其他类;而组件B定义为用户维护组件,我们就应当在组件B中包含所有实现用户维护的必需的类和接口,使用者可以通过组件B完成维护用户的功能而无须访问组件外的其他类。

3.10.2 独立性
独立性是说,组件应当是可以独立部署的,与其他组件无依赖关系,最多仅保持关联关系。例如可以把组件A部署到服务器1,把组件B部署到服务器2,虽然组件A和B都共同使用用户数据,但是A与B之间无依赖关系。也就是说,组件与组件之间应当是松耦合关系。

3.10.3 逻辑性
逻辑性是说,组件是从软件构件设计的观点来定义的,并非从需求中可以直接导出。组件建立在系统分析和设计的基础上,对已经实现的功能进行逻辑划分。组件的定义是为了规划系统结构,将一个复杂的系统分解为一个个具有完备功能的、可独立部署的小模块。这些小模块可大可小,从理论上说,可任意选择一部分功能定义一个组件。

3.10.4 透明性
透明性是说,组件的修改应当只涉及组件的定义以及组件中所包含的类的重新指定,而不应当导致类的修改。例如当一个组件的功能变化时,它所包含的类可能从原来的类A、类B、类D变成类B、类C、类D,但是类A、B、C、D都不应当被修改。

3.10.5 使用组件
■ 分布式应用
在分布式应用的情况下,系统的功能可能被部署在异构环境下,一个业务目标可能需要经历两个甚至多个节点才能完成。这时我们需要将实现业务目标的那些类和接口规划成一些组件,每个组件完成这个业务目标中的一部分功能。这些组件可被独立部署在不同的节点上,相互之间通过既定的通信协议交互来完成业务目标

■ 应用集成
在应用集成项目中,经常面临新业务和遗留系统问题。新业务需要调用遗留系统的功能,但是又不能修改遗留系统。原因可能是修改遗留系统的代价高昂,也可能是结构差异导致新旧系统无法直接通信。不管什么原因,为了保证遗留系统能够被集成到新系统中,一个解决方案就是在新系统中规划出一些组件,这些组件所拥有的接口完成遗留系统的功能。新系统是的其他业务模块与这些组件交互,而这些组件则拥有遗留系统的代码或者通过某种方式(代理模式、适配置器等)使用遗留系统,如图3.31所示。

■ 第三方系统

如果在建设的项目中,有第三方系统要访问本系统,出于松耦合的考虑,让第三方系统直接使用或者说把本系统中的类直接暴露给第三方系统是很糟糕的设计。因此,有必要将本系统要提供给第三方系统使用的功能定义成一系列组件,让第三方通过组件来访问本系统。在这些组件中,除了包含本系统的实现类外,还可以根据实际情况通过提供这些实现类的代理、适配器、消息中间件等手段来解耦第三方系统对本系统的依赖,如图3.32所示。

■ SOA服务
实际上组件的概念非常类似于SOA的服务。如果要开发一个SOA架构的应用系统,那么开发SOA服务的过程实际上就是定义组件的过程。在SOA架构下,系统功能由一个个的服务向外部暴露,也就是说,系统被定义成一个个的组件。这些服务是松耦合的,它们之间通过企业总线交互以完成业务功能,如图3.33所示。

3.11 节点

节点是带有至少一个处理器、内存以及可能还带有其他设备的处理元素。在实际工作中,一般说来服务器、工作站或客户机都可以称为一个节点。节点是应用程序的部署单元。节点元素特别用于部署视图,描述应用程序在物理结构上是如何部署在应用环境中的,是一种包括软、硬件环境在内的拓扑结构描述。
一般来说,以下两种情况下需要使用到节点元素。

3.11.1 分布式应用环境
在分布式应用环境中,通常会有多于一个的服务器、处理设备或者中间件。所开发出的应用程序会部署到这些不同的服务器或处理节点上,通过描述这些服务器之间的调用和依赖关系以表达应用环境的拓扑结构。图3.34展示了一个客户服务分布式应用系统的节点拓扑视图。
3.11.2 多设备应用环境
如果应用环境中包括多种硬件设备,为了表达这些硬件设备的结构,应当使用节点元素来绘制部署视图。图3.35展示了ATM应用环境中的节点和设备结构,它来自统一过程的官方文档。

只供参考,喜欢请支持正版图书

《大象:thinking in uml 》(第二版) 3章 UML核心元素 8-11节 设计类、关系、组件、节点相关推荐

  1. 《大象:thinking in uml 》(第二版) 3章 UML核心元素 3节 用例

    只供参考,喜欢请支持正版图书 3.3 用例 用例在UML建模中是最最重要的一个元素.之所以说它重要,是因为UML是面向对象的,除用例之外,所有其他元素都是"封装"的."独 ...

  2. 《大象:thinking in uml 》(第二版) 3章 UML核心元素 1-2节 版型、参与者

    只供参考,喜欢请支持正版图书 3.1 版型 在UML里有一个概念叫版型(stereotype),有些书里也称为类型.构造型.这个概念是对一个UML元素基础定义的扩展,在同一个元素基础定义的基础上赋予特 ...

  3. 《大象:thinking in uml 》(第二版) 5章 UML核心模型

    只供参考,喜欢请支持正版图书 5.1 用例模型概述 用例模型的好坏将决定整个开发过程的好坏. 用例模型是系统既定功能及系统环境的模型,它可以作为客户和开发人员之间的契约.用例是贯穿整个系统开发的一条主 ...

  4. 《大象--Thinking in UML 第二版》已于近日在当当首发,同时邀请各位加入新浪微博[大象-thinkinginUml群]:http://q.weibo.com/1483929

    <大象--Thinking in UML 第二版>已于近日在当当首发,感兴趣的朋友可以去看看http://product.dangdang.com/product.aspx?product ...

  5. 数据结构(C语言)第二版 第一章课后答案

    数据结构(C语言)第二版 第一章课后答案 这本书,我以后也会用,所以趁着考完试做个整理,顺便分享出来.电子资源发不出来,放评论区吧,有需要自取. 1. 简述下列概念:数据.数据元素.数据项.数据对象. ...

  6. 《Adobe Illustrator CS6中文版经典教程(彩色版)》—第0课0.11节创建与应用图案...

    本节书摘来自异步社区<Adobe Illustrator CS6中文版经典教程(彩色版)>一书中的第0课0.11节创建与应用图案,作者[美]Adobe公司,更多章节内容可以访问云栖社区&q ...

  7. 《大象:thinking in uml 》(第二版) 9章 获取需求 5-8节 领域建模、提炼业务规则、获取非功能性需求、主要成果物

    只供参考,喜欢请支持正版图书 9.5.2 现在行动:建立领域模型 建立领域模型首先要确定领域,才能为之建模.何为领域?所谓领域就是我们分析问题时将整体分解以后的相对独立的部分 在实际工作中,并不需要把 ...

  8. 《大象:thinking in uml 》(第二版) 9章 获取需求 1-2节 定义边界、发现主角

    只供参考,喜欢请支持正版图书 9.1 定义边界 边界定义的不同会带来不同的结果,因为视角会因边界而变动.那么有没有一种方法能帮助我们定义边界呢?有,通过前景文档当中的业务目标来定义边界会是一个好办法, ...

  9. Camel实战第二版 第一章 初识Camel

    目录 第一部分:迈出第一步 第一章:初识Camel 第二章:Camel路由 本章包含: Camel介绍 Camel的主要功能 初次使用Camel Camel的架构与概念 从零开始构建一个复杂的系统代价 ...

最新文章

  1. Maven build标签
  2. iOS多线程拾贝------操作巨人编程
  3. HDU-1128 Self Numbers 筛选
  4. java.lang.IllegalArgumentException: Path index does not start with a / character
  5. 一款小巧好用的MAC地址扫描器
  6. 智能判断图片中是否存在某物体_智能家居组件漫谈——人体传感器
  7. 重读经典:完全解析特征学习大杀器 ResNet
  8. 简单选择排序算法 (JAVA)
  9. 《R与Hadoop大数据分析实战》一1.6 HDFS和MapReduce架构
  10. HTML5 — 知识总结篇《VIII》【媒体元素】
  11. IDEA运行main,junit方法报错Class not found
  12. 用springMVC拦截器实现操作日志管理功能
  13. 鸟哥惠新宸:PHP 7.1 的新特性我并不是很喜欢
  14. 无法打开登录所请求的数据库 。登录失败。用户*登录失败。解决办法
  15. 人大金仓命令行客户端工具KSQL系列2
  16. 颜色模型HSB-这么强大的工具,你居然还不知道?
  17. Qt QSS之QSlider滑动条美化
  18. nuxt百度地图引入基础使用,百度地图初始化,行政区划划分
  19. 排序不等式的两种证明方法
  20. 自主研发项目四之微信上门洗车III

热门文章

  1. 获取外网IP地址API
  2. Pytorch/Python计算交并比IOU(IU)代码(批量算IOU)
  3. 浅谈如何进行技术选型 (以工作流引擎技术选型为例说明)
  4. 侧边栏php,WordPress中用于创建以及获取侧边栏的PHP函数讲解
  5. Unusual Sequences (隔板法+dp)
  6. 用于主动学习的顺序图卷积网络(论文解读)
  7. 数据库SQL语言学习--上机练习4(视图)(持续更新中)
  8. JS实践(乾坤未定,你我皆是黑马4-3)
  9. Js 中文显示星期几方法
  10. 执业医师考试该如何备考?