您现在的位置是:首页 >技术交流 >设计模式:创建型设计模式、结构型设计模式网站首页技术交流

设计模式:创建型设计模式、结构型设计模式

Polymer to C++ 2023-06-28 20:00:03
简介设计模式:创建型设计模式、结构型设计模式

前言

如何学习设计模式?

  1. 明确目的
  • 在现有的设计模式上,拓展代码
  • 在做功能抽象时,如何选择设计模式
  1. 学习步骤(由理解到使用)
  • 该设计模式解决了什么问题:稳定点、变化点
  • 该设计模式的代码结构是什么
  • 该设计模式符合哪些设计原则
  • 该设计模式上如何拓展代码
  • 该设计模式有哪些典型应用场景

设计模式基础

  • 设计模式是指在软件开发过程中,经过验证的,用于在特定环境下,重复出现的,特定问题的解决方案
  • 设计模式是在满足设计原则后,慢慢迭代出来的
  • 具体需求既有稳定点,又有变化点,才可以使用设计模式,期望修改少量的代码,就可以适应需求的变化
  • 软件开发好比在一个房间里,有一只好动的猫,如何使房间保持整洁:把猫关在笼子里;使用设计模式,让变化点只能在有限的范围内变化
  • 设计模式是与语言的特点有关的,就C++而言,面向对象的思想:封装(隐藏实现细节,实现模块化)、继承(无需修改原有类的情况下,通过继承实现对功能的扩展)、多态(函数重载、虚函数的重写…)

设计原则

  1. 依赖倒置:高层模块不应该依赖底层模块,两者都应该依赖抽象;抽象不应该依赖具体实现,具体实现应该依赖于抽象。
  2. 开放封闭:一个类应该对拓展(组合和继承)开放,对修改关闭。(封装、多态)
  3. 面向接口:不将变量类型声明为某个特定的类,而是声明为某个接口;使用者无需知道对象的具体类型,只需要知道对象所具有的接口;减少系统中各部分的依赖关系,从而实现“高内聚,松耦合”的类型设计方案(封装)
  4. 封装变化点:将稳定点和变化点分离,扩展修改变化点,让稳定点和变化点的实现层次分离(封装、多态)
  5. 单一职责:就一个类和接口而言,应该仅有一个引起它变化的原因(封装)
  6. 里氏替换:任何基类可以出现的地方,子类一定可以出现;子类必须完全实现父类的抽象方法,但不能覆盖父类的非抽象方法(多态)
  7. 接口隔离:类封装(权限限定词来实现),类与类的依赖通过接口进行联系(依赖注入)。
  8. 组合优于继承
  9. 最小知道原则:不对外暴露不需要的接口 。(封装)

设计原则之间有概念重复的部分,侧重点又有所不同;符合设计原则的代码,只需要修改少量的代码就可以演变成设计模式;因此在开发时,应参考设计原则进行代码结构设计

一. 创建型设计模式

1. 模板方法

  • 定义一个操作中算法的骨架,而将一些步骤延迟到子类中;基类中有骨架流程接口,所有子流程对子类开放并且是虚函数
  • 稳定点:算法的骨架,变化点:子流程需要变化
  • Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
  • 最常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富
  • 反向控制流程的经典应用
  • 父类protected保护子类需要复写的子流程;这样子类的子流程只能父类来调用

2. 观察者模式

  • 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新
  • 稳定点:“一”对“多”的依赖关系,“一”变化“多”跟着变化;变化点:“多”的数量增加或减少
  • 观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合
  • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了
  • 观察者不依赖通知顺序,目标对象也不知道通知顺序
  • 常用在基于事件的UI框架中,也是MVC的组成部分
  • 常用在分布式系统中、actor框架中

3. 策略模式

  • 定义一系列算法,把它们一个个封装起来,并且它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化
  • 稳定点:客户程序对算法的调用关系;变化点:算法
  • 可以用来消除大量出现的if、else
  • 策略模式提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换
  • 策略模式消除了条件判断语句;也就是在解耦合

二. 结构型设计模式

1. 单例模式

  • 保证一个类仅有一个实例,并提供一个该实例的全局访问点
  • 稳定点:类只有一个实例,提供一个全局访问点 ;变化点:有多个类只需要创建一个实例,如何达成复用
  • 禁用拷贝构造,拷贝赋值,移动构造,移动赋值

2. 工厂模式

  • 定义一个用于创建对象的接口,让子类决定实例化哪一个类
  • Factory Method使得一个类的实例化延迟到子类
  • 稳定点:创建同类对象的接口(提供对象创建接口),并且同类对象只有一个相同的职责(提供一个功能接口);变化点:同类对象的个数
  • 通常应用在创建过程比较复杂,并且希望对外隐藏这些细节的场景
  • 比如连接池、线程池
  • 希望隐藏对象的真实类型
  • 对象创建会有很多参数来决定如何创建
  • 创建对象有复杂的依赖关系

3. 抽象工厂

  • 提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类
  • 稳定点:创建同类对象的接口(提供对象创建接口),并且同类对象有多个相同的职责(提供多个功能接口);变化点:同类对象的个数
  • 如果没有应对“多系列对象创建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂即可
  • “系列对象”指的是在某个特定系列的对象之间有相互依赖、作用的关系。不同系列对象之间不能相互依赖
  • 主要应用在“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动

4. 责任链

  • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
  • 稳定点:请求按链条传递,并且会在链条的某一点上被处理;变化点:处理节点的个数,处理节点的顺序,处理的条件
  • 解耦请求方和处理方,请求方不知道请求是如何被处理的,处理方的组成是由相互独立的子处理构成,子处理流程通过链表的方式连接,子处理请求可以按任意顺序组合
  • 责任链请求强调请求最终由一个子处理流程处理;通过了各个子处理条件判断
  • 责任链扩展就是功能链,功能链强调的是,一个请求依次经由功能链中的子处理流程处理

5. 装饰器

  • 动态地给一个对象增加一些额外的职责。就增加功能而言,装饰器模式比生产子类更为灵活
  • 稳定点:要为一个类增加职责且顺序无关;变化点:类的职责
  • 通过组合而非继承的手法,装饰器模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生”问题
  • 不是解决“多子类衍生”问题,而是解决“父类在多个方向上扩展功能”的问题
  • 装饰器模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,实现复用装饰器的功能
  • 什么时候使用:在不影响其他对象的情况下,以动态、透明的方式给对象增加职责;每个职责都是完全独立的功能,彼此之间没有依赖

6. 组合模式

  • 将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性
  • 稳定点:层次关系稳定,对象和组合对象的使用具有一致性;变化点:对象的职责变更,组合对象里的对象数量变更
  • 如果想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来,使得层次结构实现更简单,从外部来使用这个层次结构也更容易
  • 如果希望统一地使用组合结构中的对象,可以选用组合模式,这正是组合模式提供的主要功能
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。