博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【面向对象分析与设计】读书笔记2-类与对象

Posted on 2011-05-30 16:26  李大嘴  阅读(637)  评论(0编辑  收藏  举报

类间关系

在类图中,除了需要描述单独的类的名称、属性和操作外,我们还需要描述类之间的联系,因为没有类是单独存在的,它们通常需要和别的类协作,创造比单 独工作更大的语义。在UML类图中,关系用类框之间的连线来表示,连线上和连线端头处的不同修饰符表示不同的关系。类之间的关系有继承(泛化)、关联、聚 合和组合。

(1)继承:指的是一个类(称为子类)继承另外的一个类(称为基类)的功能,并增加它自己的新功能的能力,继承是类与类之间最常见的关系。类图中继 承的表示方法是从子类拉出一条闭合的、单键头(或三角形)的实线指向基类。例如,图1给出了MFC中CObject类和菜单类CMenu的继承关系。

类的继承在C++中呈现为:

class B { }
 class A : public B{ }

没有多态的继承是可能的,但它的用处不大。

(2)关联:指的是模型元素之间的一种语义联系,是类之间的一种很弱的联系。关联可以有方向,可以是单向关联,也可以是双向关联。可以给关联加上关 联名来描述关联的作用。关联两端的类也可以以某种角色参与关联,角色可以具有多重性,表示可以有多少个对象参与关联。可以通过关联类进一步描述关联的属 性、操作以及其他信息。关联类通过一条虚线与关联连接。对于关联可以加上一些约束,以加强关联的含义。
 
 关联在C++中呈现为:

class A{...}
 class B{ ...}
 A::Function1(B &b) //或A::Function1(B b) //或A::Function1(B *b)

即一个类作为另一个类方法的参数。

(3)聚合:指的是整体与部分的关系。通常在定义一个整体类后,再去分析这个整体类的组成结构。从而找出一些组成类,该整体类和组成类之间就形成了 聚合关系。例如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。需求描述中“包含”、“组成”、“分为…部分”等词常意味着聚合关系。

(4)组合:也表示类之间整体和部分的关系,但是组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在。部分对象与整体对象之间具有共生死的关系。

聚合和组合的区别在于:聚合关系是“has-a”关系,组合关系是“contains-a”关系;聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象, 同时也就删除了代表部分事物的对象。

我们用浅显的例子来说明聚合和组合的区别。“国破家亡”,国灭了,家自然也没有了,“国”和“家”显然也是组合关系。而相反的,计算机和它的外设之间就是聚合关系,因为它们之间的关系相对松散,计算机没了,外设还可以独立存在,还可以接在别的计算机上。在聚合关系中,部分可以独立于聚合而存在,部分的所有权也可以由几个聚合来共享,比如打印机就可以在办公室内被广大同事共用。

在C++语言中,从实现的角度讲,聚合可以表示为:

class A {...}
 class B { A* a; .....}

即类B包含类A的指针;

而组合可表示为:

class A{...}
 class B{ A a; ...}

即类B包含类A的对象。

准确的UML类图中用空心和实心菱形对聚合和组合进行了区分。

          图2 聚合和组合

 

聚合,关联,组合 是对象之间的三种关系。从某种意义上说,继承是一种类的纵向关系,而聚合,关联,组合是对象的横向关系。


其关系强弱为 关联<聚合<组合

 

关联和聚合的区别主要在语义上,关联的两个对象之间一般是 平等的,例如你是我的朋友,聚合则一般不是平等的,例如一个公司包含了很多员工,其实现上是差不多的。聚合和组合的区别则在语义和实现上都有差别,组合的 两个对象之间其生命期有很大的关联,被组合的对象是在组合对象创建的同时或者创建之后创建,在组合对象销毁之前销毁。一般来说被组合对象不能脱离组合对象 独立存在,而且也只能属于一个组合对象,例如一个文档的版本,必须依赖于文档的存在,也只能属于一个文档。聚合则不一样,被聚合的对象可以属于多个聚合对 象,例如一个员工可能可以属于多个公司。

我想举个通俗的例子。
你和你的心脏之间是composition关系(心脏只属于自己)
你和你买的书之间是aggregation关系(书可能是别人的)
你和你的朋友之间是association关系

=========================

再辨类与类之间的关系:

类与类之间的关系对于理解面向对象具有很重要的作用,以前在面试的时候也经常被问到这个问题,在这里我就介绍一下。

类与类之间存在以下关系:      (1)泛化(Generalization)      (2)关联(Association)      (3)依赖(Dependency)      (4)聚合(Aggregation)

UML图与应用代码例子:

1.泛化(Generalization)

[泛化]

表示类与类之间的继承关系,接口与接口之间的继承关系,或类对接口的实现关系。一般化的关系是从子类指向父类的,与继承或实现的方法相反。

[具体表现]

父类 父类实例=new 子类()

[UML图](图1.1)

图1.1 Animal类与Tiger类,Dog类的泛化关系

[代码表现]

  1. class Animal{}   
  2. class Tiger extends Animal{}   
  3. public class Test   
  4. {   
  5.     public void test()   
  6.      {   
  7.          Animal a=new Tiger();   
  8.      }   
  9. }

2.依赖(Dependency)

[依赖]

对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。

[具体表现]

依赖关系表现在局部变量,方法的参数,以及对静态方法的调用

[现实例子]

比如说你要去拧螺丝,你是不是要借助(也就是依赖)螺丝刀(Screwdriver)来帮助你完成拧螺丝(screw)的工作

[UML表现](图1.2)

图1.2 Person类与Screwdriver类的依赖关系

[代码表现]

  1. public class Person{   
  2.     /** 拧螺丝 */  
  3.     public void screw(Screwdriver screwdriver){   
  4.          screwdriver.screw();   
  5.      }   
  6. }  

3.关联(Association)

[关联]

对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。

[具体表现]

关联关系是使用实例变量来实现

[现实例子]

比如客户和订单,每个订单对应特定的客户,每个客户对应一些特定的订单;再例如公司和员工,每个公司对应一些特定的员工,每个员工对应一特定的公司

[UML图] (图1.3)

图1.3 公司和员工的关联关系

[代码表现]

  1. public class Company{   
  2.     private Employee employee;   
  3.     public Employee getEmployee(){   
  4.         return employee;   
  5.      }   
  6.     public void setEmployee(Employee employee){   
  7.         this.employee=employee;   
  8.      }   
  9.     //公司运作   
  10.     public void run(){   
  11.          employee.startWorking();   
  12.      }   
  13. }  

(4)聚合(Aggregation)

[聚合]

当对象A被加入到对象B中,成为对象B的组成部分时,对象B和对象A之间为聚集关系。聚合是关联关系的一种,是较强的关联关系,强调的是整体与部分之间的关系。

[具体表现]

与关联关系一样,聚合关系也是通过实例变量来实现这样关系的。关联关系和聚合关系来语法上是没办法区分的,从语义上才能更好的区分两者的区别。

[关联与聚合的区别]

(1)关联关系所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种关联关系,而不是聚合关系,因为人不是由自行车组成的。

聚合关系涉及的两个对象处于不平等的层次上,一个代表整体,一个代表部分。比如电脑和它的显示器、键盘、主板以及内存就是聚集关系,因为主板是电脑的组成部分。

(2)对于具有聚集关系(尤其是强聚集关系)的两个对象,整体对象会制约它的组成对象的生命周期。部分类的对象不能单独存在,它的生命周期依赖于整 体类的对象的生命周期,当整体消失,部分也就随之消失。比如张三的电脑被偷了,那么电脑的所有组件也不存在了,除非张三事先把一些电脑的组件(比如硬盘和 内存)拆了下来。

[UML图](图1.4)

图1.3 电脑和组件的聚合关系

[代码表现]

  1. public class Computer{   
  2.     private CPU cpu;   
  3.     public CPU getCPU(){   
  4.         return cpu;   
  5.      }   
  6.     public void setCPU(CPU cpu){   
  7.         this.cpu=cpu;   
  8.      }   
  9.     //开启电脑   
  10.     public void start(){   
  11.         //cpu运作   
  12.          cpu.run();   
  13.      }

=========================

在分析阶段和设计的早期阶段,开发者有两项主要任务:

从问题域的词汇表中确定出类;

创建一些结构,让多组对象一起工作,提供满足问题需求的行为。

我们将这样的类和对象统称为问题的"关键抽象",把这些协作结构称为实现的"机制"。

在开发这些的阶段中,开发者必须关注这些关键抽象和机制的外部视图。外部视图代表了系统的逻辑框架,因此包含了系统的类结构和对象结构。在设计阶段的后期以及随后的实现阶段,开发者的任务发生了变化,其关注的焦点放在了这些抽象和机制的内部视图上,包括它们的物理实现。

如何创建高质量的类与对象,有以下5个评测指标:耦合内聚充分性完整性基础性

在实际的开发过程中,设计人员做的最多的是取舍(时间与空间,聚合、继承、依赖选择等等),选择操作、选择关系、选择实现。

组件  例如,一种相当高层的抽象、一个GUI框架、一个数据库和整个库存系统在概念上都是独立的对象,但它们都不能被表示为一个单独的类 。相反,最好是将这些抽象表示为一组类,这些类的实例互相协作,提供我们所期望的结构和功能。Stroustrup将这样的一组类称为一个组件

--------------------------------------------------------------------------------------------------------------------------

对象

对象分为实体对象与控制对象,不是所有的对象都有清晰的物理边界,不是所有的名词都是都是对象,对象须具备状态、行为(协议分组)和标识符。例如,美和色彩这样的属性就不是对象,爱和恨这样的感情也不是对象。但是,这些东西有可能成为其他对象的属性。例如,我们可以说一个男人(一个对象)爱他的妻子(另一个对象),或者说某只猫(又一个对象)是灰色的。

对象之间关系

链接关系--两个对象之间物理上或概念上的联系

 
(点击查看大图)图3-5  链接

作为链接的参与者,一个对象可能扮演以下三种角色之一:

控制器  这个对象可以操作其他对象,但不会被其他对象操作。在某些地方,"主动对象"和"控制器"这两个术语是互换使用的。

服务器  这个对象不操作其他对象,它只被其他对象操作。

代理  这个对象既可以操作其他对象,也可以被其他对象操作。创建代理通常是为了表示问题领域中的一个真实对象。

如图例子中,FlowController是一个控制器对象,DisplayPanel是一个服务器对象,Valve是一个代理。展示了这些责任如何恰当地被分配在一组协作的对象中。

链接可见性与同步:

可见性:考虑两个对象A和B,它们之间存在一个链接。为了让A能向B发送一条消息,B必须以某种方式让A能看到它。在对问题分析的过程中,基本上可以忽略可见性的问题,但当开始设计具体的实现时,就必须考虑跨越链接的可见性,因为,我们此时的考虑决定了链接两端对象的范围的访问。例如:决定对象之间的关系主要是设计这些对象进行交互的机制。开发者要问的问题很简单:这些知识应该放在哪里?例如,在一份制造计划中,物料(称为批次)进入制 造车间等待处理。当它们进入特定的车间时,我们必须通知车间的经理采取相应的行动。现在面临一个设计选择:一个批次的物料进入一个车间,这个操作是车间的 操作,是批次的操作,还是两者的操作?如果我们决定它是车间的操作,那么车间就必须对批次可见。如果我们决定它是批次的操作,那么批次就必须对车间可见, 因为必须知道它进了哪个车间。最后,如果我们认为它既是车间的操作,也是批次的操作,那么就必须使它们互相可见。我们还必须决定车间和经理之间的某种可见 关系(而不是批次和经理),经理必须知道要管理的车间,或者车间必须知道它的经理。

同步:当一个对象通过链接向另一个对象发送一条消息时,这两个对象就称为同步了。在完全串行式的应用中,这种同步通常是通过简单的方法调用来完成的。但 是,在存在多控制线程的情况下,对象需要更复杂的消息传递机制来处理并发系统中可能出现的互斥问题。前面曾提到,主动对象拥有自己的控制线程,所以我们期 望它们的语义在其他对象存在时仍然得到保证。但是,当一个主动对象与一个被动对象之间有链接时,我们必须选择以下3种同步方式之一:

顺序  只有在某一时刻只存在一个主动对象时,被动对象的语义才得到保证;

守卫  在多个控制线程的程序下,被动对象的语义也能保证,但主动的客户之间必须协作,以实现互斥访问;

并发  在多个控制线程的程序下,被动对象的语义也能保证,服务提供者保证互斥。
基础性