设计模式笔记

设计模式的六大原则🔗

  • 开放封闭原则
  • 里氏替换原则(任何地方都可以用子类替代基类)
  • 依赖倒转原则(面向接口编程)
  • 接口隔离原则(使用多个接口比单个接口好)
  • 迪米特原则(一个对象应尽可能少了解其他对象)
  • 单一职责原则

创建型模式🔗

单例模式🔗

  • 作用
    • 保证一个类只有一个实例
    • 提供一个访问该实例的全局节点
  • 解决方案
    • 一般是将构造函数设为私有,并提供一个静态函数接口来调用构造函数以创建对象,并返回已创建好的单一实例
  • 优点
    • 保证所有对象都访问同一对象
    • 获得一个全局的访问节点
    • 可以节省资源,减少创建与销毁的花费
  • 缺点
    • 单例职责过重,一定程度上违背单一职责原则
    • 不适用变化的对象
    • 多线程中需考虑单例创建的竞争问题
  • 用例
    • 公用计数器
    • 线程池
    • 日志
  • C++实现
    template<typename T>
    class Singleton
    {
    public:
        static T& GetInstance()
        {
            static T instance;
            return instance;
        }
    
        Singleton(T&&) = delete;
        Singleton(const T&) = delete;
        void operator= (const T&) = delete;
    
    protected:
        Singleton() = default;
        virtual ~Singleton() = default;
    };

工厂模式🔗

简单工厂🔗

  • 一般是通过一个switch,通过判断输入的参数返回需要生成的对象
  • 缺点是容易高内聚

工厂模式🔗

  • 首先有一个基类工厂,提供一个创建对象接口,可以是一个抽象接口以强制要求子类以不同方式实现该接口,也可以返回一个默认的对象类型
  • 产生一系列子类继承该工厂与其接口,产生特定的对象
  • 优点
    • 避免创建者与具体产品之间的耦合
    • 符合开闭原则,无需更改现有客户代码就可引入新的对象

抽象工厂模式🔗

  • 抽象工厂为每个产品提供一个创造接口,不同子类实现这些接口,以达到同种产品不同产品族的效果
  • 优点
    • 避免客户端和具体产品代码的耦合
    • 符合单一职责原则,将产品生产的任务抽取至同一位置
    • 符合开闭原则,引入新的产品体系时不需要修改客户端代码

建造者模式🔗

  • 结构
    • 提供一个建造者类,其内部存储了一个需要建造的目标类
    • 提供一系列接口,用于对该目标类的各个属性进行构造
    • 提供一个获取产品接口,用于返回创建好的接口
  • 用处
    • 当希望使用代码创建不同形式的产品时,可以使用建造者模式;
      • 优点是可以自定义构造顺序,和选择必要的构造
    • 当构造函数存在很多个可选参数,一般做法可以选择重载构造函数;可以使用建造者实现分步骤生成对象,只调用需要调用的接口

原型模式🔗

  • 在需要获得一个对象的副本时使用
  • 将创建副本的任务交由原对象,提供一个clone()接口;实现该功能的对象即为原型模式
  • 原型版本一般需要重构拷贝构造函数,在clone()接口中只需要返回一个参数为原对象的new新构建的对象即可

结构型模式🔗

适配器模式🔗

  • 用于对对象接口进行转换,使其能够对一些本来不兼容的对象进行交互

桥接模式🔗

  • 用处
    • 当需要对一个对象从多个维度上进行拓展时,用组合代替继承,以防止类数量的指数型增加
  • 结构
    • 存在两个需要拓展的属性(比如说GUI与系统API),若对每个GUI与API都设计一个配套的类,那么类的数量即为 num(GUI) x num(API)
    • 我们将API类放入GUI类中,并在GUI中调用抽象的API,这样实现一个桥接,使得GUI与API能够独立实现

组合模式🔗

  • 用处
    • 用于实现一个树状结构的对象
  • 结构
    • 一个组合模式中提供了两种基本元素:简单叶节点与复杂容器,一个容器能同时包含两种元素;
    • 简单叶节点拥有统一接口,可以统一调用
    • 复杂容器中可以存储简单叶节点与复杂容器,并提供添加与删除两者的接口

装饰器模式🔗

  • 用处
    • 用于动态地为对象新增额外的行为
  • 结构
    • 装饰器和目的类具有实现同样的接口(即装饰器和被装饰类拥有同一个父类或接口),装饰器内部存储被装饰类,并实现和被装饰类相同的接口,添加需要的额外功能,都通过调用被装饰类的接口实现
    • 由于装饰器与被装饰类继承同一接口,所以装饰器内部也可储存装饰器,可以一层一层地套用装饰器

外观模式🔗

  • 用处
    • 提供一个指向复杂子系统的直接接口,减少系统直接的耦合性

享元模式🔗

  • 用处
    • 需要创建大量的类导致内存使用量过高时,可以使用享元来存储一些共享的数据

代理模式🔗

  • 结构
    • 为用户提供一个代理层接口,由代理层接管用户对底层接口的调用
  • 用处
    • 延迟初始化:由于用户首先调用代理层,所以可以延迟底层重量级服务的初始化
    • 访问控制:控制用户对底层接口的访问
    • 记录日志:可以在代理中处理用户的操作日志
    • 请求结果缓存:可以在代理中缓存一些用户请求,提高效率
    • 智能引用:可以根据客户端对底层服务的引用数量,来判断并关闭底层服务,回收资源

行为模式🔗

责任链模式🔗

  • 用处
    • 需要不同方式处理不同种类请求,且种类与顺序位置未知
    • 需要动态的决定处理单元的顺序,并动态加入或移除处理单元时
  • 结构
    • 责任链将不同的处理单元组合成链,对于一个新的请求,处理单元自行判断能否对当前请求进行处理,是否需要继续交由责任链上的其他处理节点处理;处理单元也可以自行决定是否完成处理并结束

命令模式🔗

  • 用处
    • 命令模式将一个方法的调用转化为一个对象,使其可以被保存,转移,传递
    • 可以将操作放入队列,或记录,或通过网络发送
    • 命令模式可以用于实现回滚功能
  • 结构
    • 发送者向接受者发送一条命令(命令不由发送者构造,而是存储一条提前构造的命令)
    • 命令一般都会实现一个指向命令的方法;具体命令中一般不完成工作,而是将工作传达给接受者,具体命令一般只实现发送者到接受者的细节
    • 接受者实现具体的业务逻辑

迭代器模式🔗

  • 用处
    • 将集合的遍历行为抽取为单独的迭代器行为
    • 不同容器的迭代器实现各自的遍历方式,但提供相似接口
  • 结构
    • 提供一个移动接口,用于遍历集合内各个元素
    • 提供一个获取原元素接口

中介者模式🔗

  • 用处
    • 多个对象直接停止直接交流,全部通过一个中介者进行交流

备忘录模式🔗

  • 用处
    • 用于储存一个对象的快照,用于撤回与恢复等操作
    • 在保证其私有对象的封装性的同时实现数据的保存
  • 结构
    • 由数据的原发者(即需要被快照的对象)负责创建快照的操作
    • 创建的快照拥有元数据与状态两部分,状态储存了原发者的状态,只能有原发者访问;元数据开放访问,用于储存一下基本信息(时间等)
    • 可创建一个负责人用于管理快照的存储与取出;负责人只有访问元数据的权限,状态只能有原发者访问

观察者模式🔗

  • 用处
    • 一个对象改变状态,所有订阅了该对象的订阅者都会得到通知并自动更新

状态模式🔗

  • 用处
    • 一个类拥有有限的状态,且可以迅速在各个状态间切换,并对不同状态执行不同的操作
  • 结构
    • 原始类中存储一个状态类,并实现一系列状态切换接口,不实现具体功能;具体功能交由状态类实现
    • 状态类定义各种功能接口,具体状态类则对接口进行实现

策略模式🔗

  • 用法
    • 当需要对同一方法采用不同算法或方式进行实现时,可使用策略模式进行管理
  • 结构
    • 上下文类中包含一个策略类,用于存储当前使用的策略
    • 上下文类实现策略调用接口,即根据当前使用的策略完成任务
    • 策略类中包含策略的具体实现方法
    • 客户端自行在上下文中选择需要的策略,并调用上下文中的策略调用接口,实现需要的策略

模板模式🔗

  • 用法
    • 根据一个抽象的模板来设计各个具体类

访问者模式🔗

  • 用法
    • 存在一系列不知晓具体类的元素,访问者会根据具体类执行对应的操作
    • //在我看来就是将多个类的多态实现集中到访问者类中
  • 结构
    • 存在一系列具体类,其中都实现了一个accept接口,接受一个访问者类
    • 每个accept接口的实现即为调用访问者类中实现当前具体类操作的函数
    • 遍历访问一系列具体类时,会调用它们的accept接口,然后会通过访问者类执行与当前类相应的操作