16-设计模式

设计模式是设计经验的总结。

本章只是介绍《设计模式》中最为简单、常用的几种设计模式,它们都是用来提高系统可修改性的。

1. 可修改性及其基本实现机制

image-20220619093044579

  1. 实现的可修改性:涉及到大的场景的修改
    1. 对已有实现的修改
    2. 使用接口与实现分离即可实现可修改性,对实现作修改,不影响client类
  2. 实现的可扩展性(DIP & OCP)
    1. 对新的实现的扩展
    2. 使用接口与实现分离即可实现可拓展性,增加一个新的类即可
  3. 实现的灵活性
    1. 对实现的动态配置
    2. 多态,根据对象的实际类型调用方法

1.1. 如何实现可修改性? 重要

  1. 接口与实现的分离

1.2. 如何将接口与实现的分离 – Java视角

  1. 通过接口与实现该接口的类,将接口与实现相分离
  2. 通过子类继承父类/抽象类,将父类的接口与子类的实现相分离:通过继承的方式,在一定程度上实现了接口与实现的分离,但是也使得子类继承了父类的实现,当然可以更好地重用代码,但使得灵活性略有下降【父类接口改变,子类一定会变】。

1.3. 实现接口(interface)

  1. interface:定义了规约
  2. 实现class:实现了规约

1.4. 类图中的标志物的含义

  1. 虚线箭头:依赖
  2. 实线箭头:关联(两侧写数量)
  3. 空菱形在一侧的实线箭头:聚合
  4. 实菱形在一侧的实线箭头:组合
  5. 空心实线箭头:泛化(extends)
  6. 空心虚线箭头:实现(implements)

1.5. 类图与依赖关系

  1. Client、Interface_A、Class_A1之间是什么关系?
  2. Client和Class_A1是否存在依赖关系?

1.6. 继承

  1. 父类定义了规约(contract)
  2. 子类实现了规约(contract)

1.7. 类图与依赖关系

  1. Client、Super_A、Sub_A1之间是什么关系?
  2. Client和Sub_A1是否存在依赖关系?

1.8. 实现的可修改性

  1. 对于实现的可修改性,无论是Class_A1还是Sub_A1的method_A方法的实现的修改都和Client中的调用代码没有任何耦合性。

1.8.1. 扩展

1.9. 实现的可扩展性

  1. 对于实现的可扩展性,我们可以通过 Class_A2还是Sub_A2的创建来实现。

1.10. 实现的灵活性

1.11. 继承的优点

  1. 虽然继承也能很好的完成接口与实现的分离,但是继承还有他独有的特征。
  2. 子类不但继承了父类的接口还继承了父类的实现,这可以更好的进行代码的重用

1.12. 继承的缺点

  1. 继承的父类与所有子类存在共有接口的耦合性。当父类接口发生改变的时候,子类的接口就一定会更改,这样就会影响到 Client代码。
  2. 而且当子类创建对象的时候,就决定了其实现的选择,没法再动态的修改。即只能单继承,不能多继承,而接口可以多实现

1.13. 组合

  1. 而利用接口的组成关系,却能在实现接口和实现的前提下,体现更好的灵活性。前端类和后端类是组合关系。前端类重用了后端类的代码。
  2. 考虑到软件工程中的人的重要性。

1.13.1. 组合的优点

  1. 前端和后端在接口上不存在耦合性。当后端接口发生改变的时候,只要在 frontend 里面修改即可,并不会直接影响到Client代码。
  2. 后端类的实现亦可以动态创建、动态配置、动态销毁,非常灵活。

2. 设计模式

2.1. Why?为什么使用设计模式

  1. 设计OO软件非常困难
  2. 设计可重用的OO软件-难度更大
  3. 经验丰富的OO设计师可以做出出色的设计
  4. 新设计师倾向于使用以前使用的非OO技术
  5. 经验丰富的设计师知道一些东西-这是什么?
  6. 专家设计师知道不能从第一原则中解决所有问题
  7. 复用的解决方案
  8. 这些模式使面向对象的设计更加灵活,优雅并且最终可重用。

2.2. 什么是设计模式

  1. 设计模式:抽象一个重复的设计结构
  2. 包含类和/或对象
    1. 依赖
    2. 结构
    3. 互动,或
    4. 约定
  3. 提炼设计经验

2.3. 模式

  1. 典型问题
  2. 设计分析
  3. 解决方案
  4. 案例

2.4. 解决方案

  1. 参与者与协作:描述了设计中涉及的各个类的组成成分,他们之间的相互关系及各自的职责和协作方式。
  2. 应用场景:描述了应该何时使用模式。它解释了设计模式所要解决的问题,以及解决这个问题时所面临的特点的环境、限制条件、场景等。这也是我们在应用某种模式之前,需要仔细去体察的。
  3. 使用注意点:因为模式只是一个模板,他可以应用与多种不同场合,所以解决方案并不描述一个具体的实现,而是提供解决方案的一个抽象模型。要根据具体情况来具体实现

3. 设计模式和策略配对

:star:详见书本 p265

各个设计模式使用的设计原则:

  1. 策略模式:【强调行为的不同

    1. 减少耦合:减少策略的使用类和策略的实现类直接的耦合
    2. 依赖倒置:策略的使用类依赖的是策略的接口,而非策略的实现类。
  2. 抽象工厂模式:【强调对象的创建

    1. 职责抽象:抽象对于对象创建的职责
    2. 接口重用:提供对于对象创建的接口
  3. 单件模式:

    1. 职责抽象:隐藏单件创建的实现
    2. 信息隐藏
  4. 迭代器模式:【偏向于完成遍历集合这个业务

    其实是个工厂模式,用于创建 iterator,但使用得太多了,因此单独分出来作为一个模式

    1. 减少耦合:减少遍历的使用类遍历的实现类直接的耦合
    2. 依赖倒置:遍历的使用类依赖的是策略的接口,而非遍历的实现

4. 设计模式重点掌握类图 重要

  1. 策略模式:组合的方式,如果是继承也可以,但不易维护

    image-20220523151109269

  2. 抽象工厂模式

    1. 工厂模式

      image-20220523155305878

    2. 抽象工厂模式【相比于工厂模式多了一层抽象,注意这里的ConcreteFactory1和2都可以创建产品A和B】

      image-20220523155323993

      image-20220619100247892

  3. 单件模式

    1. image-20220523164747159
  4. 迭代器模式

    1. image-20220523170743369

5. 题目

  1. 给定场景,利用设计模式写出代码
  2. 给出代码,利用设计模式重写
  3. 设计模式内容具体查看