• UML中的各种关系


    各种关系

    UML中的各种关系一览表
    名称 英文名称 符号 描述 实现方法 耦合强度 举例 关键词 备注
    依赖 dependency 1.当类与类之间有使用关系
    时就属于依赖关系;2.依赖
    不具有“拥有关系”
    ,而是
    一种“相识关系”;3.一个
    类的实现需要另一个类的协助
    如果类A访问类B的属性或者方法,
    或者类A负责实例化类B
    ,具体地,
    被依赖者B是依赖者A的:1.成员
    函数返回值、2.形参、3.局部
    变量、4.静态方法调用​
    ★​ 学生使用电脑
    驾驶员使用车​
    使用关系​  
    关联 association 一种常识上的拥有关系,但
    双方又在逻辑上各自独立​
    ​关联者的某数据成员是指向被关
    联者的指针或引用
    ★★​ 老师和学生是
    双向关联​
    拥有关系
    has-a​
    细分为:单向关联、
    双向关联、
    自身关联、多维关联​
    聚合 aggregation 整体与部分的关系,且部分
    可脱离整体而单独存在
    同“关联”​ ★★★​ 汽车和轮胎​ 整体与部分
    单独存在
    contains-a​
     
    组合 composition 整体与部分的关系,但部分
    不能离开整体而单独存在
    “整体类”的某数据成员存储
    了“部分类”的实例
    ★★★★​ 人和头颅​ 整体与部分
    不能单独存在​
     
    泛化 generalization 类与类之间的继承关系,一般
    与特殊
    的关系,is a
    类继承​ ★★★★★​ 动物和猫​ 一般与特殊
    is-a​
     
    实现 implementation 类与接口之间的实现关系​ 接口实现​ ★★★★★​ 鸡和鸭都实现
    下蛋接口​
       

    联系与区别

    依赖与关联

    • 实现不同:依赖者不会将被依赖者作为自己的数据成员,但关联会。​
    • 侧重点不同:​依赖强调使用,关联强调拥有
    • 关系产生/结束时机不同:依赖关系是仅当使用关系发生(如类A的成员函数以类B的实例为参数)时而产生,伴随着使用结束而结束。关联关系当A中指向B的指针/引用被赋值时产生,当A的实例销毁时关系结束。​

    关联与聚合

    • 聚合关系是关联关系的一种,是强的关联关系。
    • 逻辑关系/层次不同:关联关系所涉及的两个类处在同一个层次上,而聚合关系中,两个类处于不同的层次上,一个代表整体,一个代表部分。
    • 指向被关联/聚合者的指针/引用的初始化/赋值形式不同(这一点并非绝对):对于关联,通过A的成员函数( void A::setB(B *pB) )来为A中指向B的指针/引用赋值;对于聚合,通过带参的构造函数( A::A(B *pb) )来初始化A中指向B的指针/引用。​
    • 联和聚合在语法上无法区分,必须考察具体的逻辑关系。

    聚合与组合

    • 整体与部分的生命周期不同:对于聚合,整体与部分的生命周期相互独立;对于组合,整体与部分有相同的生命周期,其中代表整体的对象负责代表部分的对象的生命周期。​
    • 构造函数不同:聚合类的构造函数中包含另一个类的实例作为参数,组合类的构造函数包含另一个类的实例化
    • 信息的封装性不同:聚合关系中,客户端可以同时了解“整体类”与“部分类”;组合关系中,客户端只认识“整体类”,“部分类”被严密封装在“整体类中”,对客户端不可见。

    举例说明

    下图是设计模式中命令模式(Command Pattern)的UML类图和C++实现,其中代码“改编”自这个开源项目。不熟悉命令模式的读者请自行学习。​

     1 #include <iostream>
     2 
     3 class Receiver
     4 {
     5 public:
     6     void Action()
     7     {
     8         std::cout << "Receiver: execute action" << std::endl;
     9     }
    10     // ...
    11 };
    12 
    13 class Command
    14 {
    15 public:
    16     virtual ~Command() {}
    17     virtual void Execute() = 0;
    18 
    19 protected:
    20     Command() {}
    21 };
    22 
    23 class ConcreteCommand : public Command
    24 {
    25 public:
    26     ConcreteCommand(Receiver *r) : receiver(r) {}
    27 
    28     ~ConcreteCommand()
    29     {
    30         if (receiver)
    31         {
    32             delete receiver;
    33         }
    34     }
    35 
    36     void Execute()
    37     {
    38         receiver->Action();
    39     }
    40 
    41 private:
    42     Receiver *receiver;
    43 };
    44 
    45 class Invoker
    46 {
    47 public:
    48     void SetCommand(Command *c)
    49     {
    50         command = c;
    51     }
    52 
    53     void ExecuteCommand()
    54     {
    55         if (command)
    56         {
    57             command->Execute();
    58         }
    59     }
    60 
    61 private:
    62     Command *command = nullptr;
    63 };
    64 
    65 int main()
    66 {
    67     ConcreteCommand command(new Receiver());
    68 
    69     Invoker invoker;
    70     invoker.SetCommand(&command);
    71     invoker.ExecuteCommand();
    72 
    73     return 0;
    74 }
    Command Pattern

    下面结合代码进行分析:

    • 依赖:这里的 main 函数相当于 Client , main 函数中仅仅创建并使用了 Receiver 、 ConcreteCommand 、 Invoker 的实例,相当于将它们的实例作为局部变量,因此 main 函数( Client )依赖了这三个类。​
    • 关联: ​Invoker 类中有指向 Command 或其子类的指针,该指针通过成员函数 void SetCommand(Command *c) 被赋值,因此 Invoker 关联了 Command 。
    • 聚合: ConcreteCommand 类中有指向 Receiver 的指针,该指针通过构造函数 ConcreteCommand(Receiver *r) 在 ConcreteCommand 实例被创建的同时被初始化,因此,ConcreteCommand聚合了Receiver。​

    需要特别说明的是,受GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》误导,几乎所有资料的UML类图都将 Ivoker 和 Command 画成了聚合关系,而将 ConcreteCommand 和 Receiver 画成了关联关系(正好跟上图相反),这显然是不对的。理由如下:​

    • “绑定”关系的牢固性不同:
      •  Invoker 和 Command 通过 void Invoker::SetCommand(Command *c) 建立关系,并且,通过多次调用函数,可以对 Invoker::command 这个成员变量赋不同的值,即 Invoker 实例可以“绑定”不同的 Command 实例,这种“绑定”是不稳定的,可更换的。​
      • 反观 ConcreteCommand ,它通过自身的构造函数 ConcreteCommand(Receiver *r) 与 Receiver 实例“绑定”,并且 ConcreteCommand 实例一旦构造完成,它所“绑定”的 Receiver 实例就不能换成其它 Receiver 实例了。显然, ConcreteCommand 和 Receiver 之间的“绑定”关系,要比 Invoker 和 Command 之间的“绑定”关系牢固得多。​
    • 关系层次不同:
      • 普通成员函数 void Invoker::SetCommand(Command *c) 暗示 Invoker 和 Command 之间是同一个层次上的“平等”关系​。
      • 构造函数 ConcreteCommand(Receiver *r) 暗示 ConcreteCommand 和 Receiver 之间是整体与部分的关系。​

    参考

     
     
     
     
     
     
     
     
     
     
     
     
     

  • 相关阅读:
    js语法
    页面格式与布局
    css样式标签
    框架
    css样式表选择器
    最大值(东方化改题+老师给的题解)
    数字(东方化改题+老师给的正解)
    测试一下这个编辑器
    请让本题永远沉睡于此(东方化改题+给的标程)
    贪吃的yjj(东方化改题+给的标程)
  • 原文地址:https://www.cnblogs.com/zpcdbky/p/16106358.html
Copyright © 2020-2023  润新知