[面向对象程序设计] 多继承 和 类图的实现

Class Diagram

Qualified association

  • Unix文件系统
    • 每一个文件有一个唯一的整数标识符
    • 可以在不同文件夹中出现相同的文件, 也可以在同一个文件夹下用不同的文件名指向同一个文件
    • 一个文件夹下的所有名字不能相同
    • 示例如下
      在这里插入图片描述
  • 如何描述文件夹和文件直接的关联关系?
    在这里插入图片描述
  • association class 无法描述上述这种文件夹和文件的关系.
    • 如果同一个文件夹下有两个文件名指向同一个文件, 那么这两个对象就有两次关系链接

解决办法 : qualified association
在这里插入图片描述

  • 给类Directory加一个关键字, 用来唯一标识一个类File的对象
  • 给定一个Directory对象和一个关键字名字, 如果给定的名字在Directory中存在, 则会对应一个文件; 如果不存在, 则不对应文锦啊
  • 给定一个文件, 可以对应多个包含某个关键字名字的Directory对象; 也可以对应一个Directory对象中的多个关键字名字.

在这里插入图片描述

  • 给定一个University对象, 根据一个id可以对应唯一一个学生. 给定一个学生,只有唯一一个University中的一个id与之对应.
  • 上面两种方法实现的功能相同, 但是设计角度不同, 上者id是Student的属性, 下着id是University的属性.

Multiple inheritance in UML

在这里插入图片描述

  • Memo同时具有Document和Message的属性. 因此同时继承这两个类
    在这里插入图片描述
  • 当定义了一个Memo对象时, 在内存中, Document的属性会占据一块位置, Message的属性会占据一块位置, 两者共同继承的Object会占据同一块位置. (并不会存放两次, 与C++有时存放两次的实现不同)
    在这里插入图片描述

Mixin class

  • 一个附属类, 当某个类派生的对象还额外需要这个类的功能时, 才会多继承该附属类, 并不会单独从该附属类派生对象.
    在这里插入图片描述
    在这里插入图片描述
  • 在第一个实现中ChequeBook类是一个正常的类, 可以派生出对象.
  • 在第二个实现中ChequeBook类作为一个附加功能, 从设计之初就是一个Mixin class, 不会单独派生出对象(与抽象类类似). 只是在多继承中作为一个附属类继承.

Discriminator

  • 对于同一个类, 根据不同的分类标准, 派生出不同的子类
    在这里插入图片描述
  • 上面例子从生命周期 和 财务情况两方面派生出了不同的类.
  • 一个子类多继承这些不同标准的类. Report 永久的 财务相关的; Memo 临时的 财务无关的

Implementation of class diagram

Uni-directional association

  • 给定A对象可以找到B对象, 反之不行 (单向的)
    在这里插入图片描述
  • 在A中定义一个指针指向向B就可
  • multiplicity 对代码实现没有影响
  • immutable : association唯一确定的不可更改的, 只能指向唯一对象
  • mutable : association可以更改, 指向其余对象
    在这里插入图片描述
    在这里插入图片描述

Bi-directional association

  • 给定A对象可以找到B对象, 给定B对象也可以找到A对象 (双向的)
    在这里插入图片描述
    在这里插入图片描述
  • 一个账户可以拥有一个借记卡, 也可以没有
  • 一个借记卡一定可以找到一个账户
  • mutable : 给定一个账户时, 可以随意创建析构一个借记卡, 也可以更改绑定的对象
  • immutable : 给定一个借记卡对象, 任何时刻一定唯一对应一个账户, 不可更改.
    在这里插入图片描述
  • 在DebitCard的构造函数中设置了指针, 只要DebitCard对象一创建, 就和一个Account绑定了, 并且没有任何成员函数对其进行更改, 因此是immutable.
  • 在Account中通过一个函数来设置DebitCard指针的指向, 可以随意创建析构来更改, 因此是mutable.
    在这里插入图片描述
  • 如何可以在类的设计时避免上述错误?
    在这里插入图片描述是
    在这里插入图片描述
  • addCard函数创建了一个DebitCard对象, 同时把自己Account对象的this指针传了过去, DebitCard对象指向了Account对象
  • addCard函数将创建的DebitCard对象指针赋值给了Account里的DebitCard指针变量, 因此Account对象也指向了DebitCard对象
  • 这两个指向通过一个函数就完成了, 更不容易出错.
  • 将DebitCard构造函数私有化, 使得用户无法自己直接创建DebitCard对象, 避免出错
  • 而由于Account中会创建DebitCard对象, 需要调用构造函数, 因此在DebitCard中声明友元Account
  • 在这个方法中, Account每创建一个新的DebitCard对象, 就是和一个新的对象关联, 因此是mutable

both directions are immutable

在这里插入图片描述

  • 给定一个账户, 担保人永远不可更改
  • 给定一个担保人, 它一定对应一个account, 并且永远只能为一个account担保, 一旦担保就不可更改.
    在这里插入图片描述
    在这里插入图片描述
  • 上述代码试图通过一句构造含糊实现双向不可更改的链接
    • 但是C++运行时, 会有一个运行顺序, 会首先构造new Guarantor, 这个时候a还是个也野指针.
    • C++中无法通过类的设计确保双向不可更改的链接, 只能由用户小心创建

Implementing qualifiers

在这里插入图片描述

  • map : 一个容器, 容器中每个元素由<key, value>组成
    • map的使用如下
      在这里插入图片描述
      在这里插入图片描述

Implementation of association classes

  • 把association class转换成一个普通的类, 这个类关联原来两个类
  • 原来两个类的原生接口不能被改变
  • 具体实现改变了原生类图的含义, 需要在实现中进一步限制说明
    在这里插入图片描述
  • 在C++中并没有和association class对应的概念, 因此将上面的类图转换为下面一种形式的类图.
  • 虽然做了转换, 根据转换后的形式进行代码实现, 但是也需要尽量保留原来类图的设计意图
  • 注意Module中的enrol函数,显示的是Module和Student的关联关系, 表达的是设计阶段的意图, 设计阶段中Module和Student有直接关联.
    • 但是在代码的具体实现中是通过Student对象构造一个Registration对象, 然后将registration对象添加到Module中 (按照下面一种转换后的形式来实现)

猜你喜欢

转载自blog.csdn.net/weixin_40996518/article/details/106336455
今日推荐