设计模式的作用

  • 应用程序
  • 框架
  • 设计模式
    • 创建模式,结构模式,行为模式
  • OOD 原则
    • OCP、DIP、LSP、SRP、ISP
  • OOD 的目标
    • 强内聚、低耦合的程序

设计模式的定义

什么是设计模式?

  • 每一个模式都描述了一种问题的通用解决方案。这种问题在我们的环境中,不停的出现。
  • 设计模式是一种可重复使用的解决方案。

一个设计模式的四个部分:

  • 模式的名称-少量字组成,有助于表达设计。
  • 待解问题-何时用以及运用模式的环境
  • 解决方案-组成设计的元素、关系、职责以及合作。抽象,不代表具体的实现。
  • 结论-该方案带来的利弊。指对系统的弹性、扩展性、和可移植性的影响。

设计模式的分类

从功能分

  • 创建模式
    • 对类的实例化过程的抽象。
  • 结构模式
    • 将类或者对象结合在一起形成更大的结构。
  • 行为模式
    • 对在不同的对象之间划分责任和算法的抽象化。

从方式分

  • 类模式
    • 以继承的方式实现模式,静态的。
  • 对象模式
    • 以组合的方式实现模式,动态的。

排序问题-如何创建一个对象

截屏2020-07-01 上午8.56.11.png

简单工厂模式

优点:

  • 使 Client 不再依赖 Sorter 的具体实现
  • 对 Client 实现 OCP - 增加 Sorter 不影响 Client

缺点:

  • 对 Factory 未实现 OCP - 增加 Sorter 需要修改 Factory

简单工厂模式-改进一

写死的类加载获取。

解决的问题:

  • 增加 Sorter 实现时,不需要修改 Factory
  • 但仍然要改 Client

其它问题

  • 丧失了编译时的类型安全
    • Client 和 Factory 均类型不安全
  • Client 仍然知道 Sorter 的实现是什么
  • 限制了 Sorter 的实现智能通过默认构造函数创建

简单工厂模式-改进二

优点:

  • 满足 OCP
    • 对 Client 和 Factory 均满足
    • 满足 OCP 方法
      • 抽象
      • 动态变成(将编译时类型检查转变成运行时检查)

缺点:

  • 缺少编译时类型安全
  • 限制了 Sorter 的实现智能通过默认构造函数创建

该机制解决了简单工厂模式最致命的问题

Singleton 单例模式

为什么要使用

Singleton 模式保证产生单一实例,即一个类只产生一个实例。

  • 减少实例频繁创建和销毁带来的资源消耗;【性能需求】
  • 当多个用户使用这个实例时,便于统一控制(比如打印机对象)【功能需求】

实现方法

  1. 静态类
  2. 加锁懒加载初始化

说明

  1. 必须有私有构造函数,保证类实例通过公有方法获取。
  2. 方法2要加锁关键字,避免产生多重实例。
  3. 尽量使用方法1构造单实例。
  4. 单例中成员变量是多线程重用的,可能会产生意想不到的结果,因此尽量将单例设计为无状态对象(只提供服务,不保存状态)。

适配器模式

截屏2020-07-02 上午7.54.08.png

适配器的作用:

  • 系统需要使用现有的类,而这个类的接口与我们所需要的不同
    • 例如:我们需要对 List 进行排序,但是我们需要一个 Sortable 的接口,原有的 List 即可不能满足要求。

适配器的应用:

  • JDBC Driver
    • 对具体数据库的适配器
    • 比如,将 Oracle 适配到 JDBC 中
  • JDBC-ODBC Bridge
    • 是将 Windows ODBC 适配到 JDBC 接口中

JUnit 中的设计模式

实现一个单元测试的步骤

  • 创建测试类,从 TestCase 派生
  • 初始化:覆盖基类的方法:setUp
  • 清除环境:覆盖基类方法:tearDown
  • 书写测试方法:命名规则:testXyz

JUnit 单元测试是如何执行的

定义抽象类,继承 Assert,实现 Test。

截屏2020-07-02 上午8.01.34.png

模板方法模式

  • 扩展功能的最基本模式之一
  • 「类的行为模式」

通过继承的方式来实现扩展

  • 基类负责算法的轮廓和骨架
  • 子类负责算法的具体实现

组合 vs 继承

  • 基于继承的模板方法比组合更容易实现
  • 适当使用这种模式。

模板方法的形式

  • 抽象方法
    • abstract void step1()
    • 强制子类实现该步骤
  • 具体方法
    • void doSomething(){…}
    • 子类不需要覆盖,但也可以覆盖之
    • 如想明确告诉子类「不需要覆盖它」,最好标明:final
  • 钩子方法
    • void setUp(){}
    • 空的实现(缺省适配器模式)
    • 子类可选择性地覆盖之,以便在特定的时机做些事。

举例

  • Java Servlet 中模板方法
  • JUnit 模板方法

策略模式

  • 扩展功能的另一种最基本的模式
  • 「对象的行为模式」

通过组合的方式来实现扩展。

三、设计模式 - 图4

使用场景

  • 系统需要在多种算法中选择一种
  • 重构系统时
    • 将条件语句转换成对于策略的多态性调用
  • 策略模式的优点(对比模板方法)
    • 将使用策略的人与策略的具体实现分离
    • 策略对象可以自由组合
  • 策略模式的问题
    • 仅封装了算法的具体实现,方便添加和替换算法。但它并不关心何时使用算法,必须由客户端来决定。

组合模式

  • 对象的结构模式

举例:

  • 文件系统
  • AWT 控件

装饰器模式

  • 对象的结构模式

作用:

  • 在不改变对客户端接口的前提下(对客户端透明)
  • 扩展现有对象的功能

装饰器模式也被笼统地称为「包装器」(Wrapper)

  • 适配器也被称为包装器,区别在于适配器是转换成另一个接口,而装饰器是保持接口不变。
  • 包装器形成一条链。

装饰器和模板、策略比较

  • 装饰器保持对象的功能不变,扩展其外围的功能
  • 模板和策略保持算法的框架不变,扩展其内部实现

装饰器和继承的比较

  • 都可以扩展对象的功能
  • 但装饰器是动态的,继承是静态的
  • 装饰器可以任意组合
    • 但这也使装饰器更复杂,有可能会组合出荒谬的结果

装饰器应用:

  • Java servlet: HttpServletRequest/HttpServletRequestWrapper
  • 同步化装饰器:Collections.synchronizedList(list)
  • Java IO 类库:
    • InputStream、OutputStream:字节流
    • Reader、Writer:字符流

Spring 中的设计模式

依赖注入 DI 与控制反转 IOC

截屏2020-07-02 上午8.49.37.png

截屏2020-07-02 上午8.51.36.png

截屏2020-07-02 上午8.53.51.png

截屏2020-07-02 上午8.55.21.png