设计模式

设计模式目录:22种设计模式open in new window

设计原则

软件模式的产生是因为变化的东西太多,为减轻人类的负担, 将一些不变的东西先用模式固化,这样让人类可以更加集中精力对付变化的东西,所以 在软 件中大量反复使用模式(我个人认为这样的软件就叫框架软件了,比如 J2EE),不但没阻碍软 件的发展,反而是推动了软件的发展.因为其 他使用这套软件的人就可以将更多精力集中在 对付那些无法用模式的应用上来.

image.png

基于六大设计原则:

  • Single 单一职责 如写函数、职责过多
  • Open 开放封闭 对扩展开放,对修改封闭
  • Liskov 里氏替换 程序里的对象都应该可以被它的子类实例替换而不用更改程序.
  • Law of Demeter 迪米特法则 最少知识 直接交流、减少耦合
  • Interface 接口隔离 尽可能小的接口, 多个专用的接口比一个通用接口好。
  • Dependency 依赖倒置 面向接口而非面向实现类,高层模块依赖于抽象而非细节 反向依赖

里氏替换

可替代性(指父类?)

讲继承复用,对开闭原则的补充

子类可以扩展父类的功能,但不能改变父类原有的功能

发布订阅

又叫观察者模式,定义对象间一对多的依赖关系,当一个对象状态发生变化时,所有依赖它的对象都将得到通知。JS 中就是事件模型。

  • 订阅者,数据结构是事件名到事件回调列表的映射,有一个默认事件any
  • 两个方法,一个是接收,参数是回调和事件名,一个是发出,参数是消息和事件名
    • 接收, 就是写入订阅表,注意事件名存在时,回调列表要 push
    • 发出,遍历调用事件名下的回调
  • 观察者模式中观察者和被观察者是互相知道对方的存在的,而在发布订阅模式中是不知道的。
  • 数据驱动,数据与逻辑分离,前因后果
  • 扩展:MVC,MVVM
class Event {
  constructor() {
    this.cacheList = new Map()
  }

  on(type, fn) {
    if (!this.cacheList.get(type)) {
      this.cacheList.set(type, [fn]) // 注意这里设置的值是数组
    } else {
      this.cacheList.get(type).push(fn)
    }
  }

  emit(type, data) {
    if (!this.cacheList.get(type)) throw 'event not found'
    for (let fn of this.cacheList.get(type)) {
      fn(data)
    }
  }
}

let event = new Event()

event.on('click', data => console.log(`event data: ${data}`))
event.emit('click', 'hello') // event data: hello

ES6 语法实践,用 ES6 重写《JavaScript Patterns》中的设计模式 - CNode 技术社区open in new window

策略模式

算法实现和使用分离,策略类可互换,环境类接受用户请求,将请求委托给策略类

将不变的部分和变化的部分分隔开来是每个设计模式的主题。

至少两部分组成: 一是策略类,策略类封装了具体的算法,并负责具体的算法。 二是环境,类 Context 接受客户的请求,随后把请求委托给某一个策略类。

// 计算工资
var strategies = {
  S: function(salary) {
    return salary * 4
  },
  A: function(salary) {
    return salary * 5
  },
  B: function(salary) {
    return salary * 6
  },
}

var calculateBonus = function(level, salary) {
  return strategies[level](salary) // Context
}

console.log(calculateBonus('A', 5000))
console.log(calculateBonus('S', 5000))

工厂方法

将创建实例的责任与使用实例的责任分开

拿到商品,不需要关心怎么生产

单例

单一实例对象 对外提供全局访问

创建对象,管理单例两个职责分离

建造器

将复杂对象的创建逻辑与最终表现分离 JS 中很少用

适配器

通过对象包装,解决接口数据结构不匹配,不改变已有接口,实现协同

代理

为对象提供一个代用品或占位符,以便控制对它的访问

  • 场景:
  • 优点:
  • 分类:
    1. 虚拟代理,把开销大的对象,延迟到真正需要时创建。 比较常用,如实现图片预加载,对象 A 负责创建 img 标签、设置 src,代理对象 B 设置 loading 图,监听 onload 后调用 A
    2. 保护代理,权限控制

合成复用原则

装饰器、包装

decorator/wrapper

在不改变元对象的基础上,对对象进行包装和拓展

将不同职责的代码装饰合并 不改变原有代码,直接修改原函数违返开闭原则

跟代理像,但代理是不方便访问本体,装饰是不确定全部功能

应用:

  • 分离业务代码和数据统计代码 Function.after
  • 分离表单校验和合并 Function.before

享元

flyweight

内部状态相同、共享 对象太多,用时间换空间的性能优化

模板方法

两部分:抽象父类和具体实现子类,父类封装子类的算法框架,子类继承

从多个子类中,分享共同点 轮廓,骨架,通用,穷举

命令模式

可撤销,并发,程序无状态

职责链

使多个对象都有机会处理请求,发送、多个接收解耦,传给第一个接收即可

应用:

  • 多种支付场景,优惠券,库存

notes copy

  1. 设计模式是一门语言加速程序员之间的沟通效率

  2. 设计模式为了解决特定问题每个设计模式都有优缺点,使用就要付出缺点的代价 a. Eg 观察者模式 i. 优点

    1. 解耦 ii. 缺点
    2. 观察者模式会导致代码可读性和维护性下降
  3. 设计模式只做一件事 a. 控制逻辑集中化

  4. 设计模式分为三种 a. 创建 i. 目的为了创建对象 ii. 创建对象配置化,本身就是满足了设计模式的精髓,目的是灵活 b. 结构

    1. 处理类或对象的组合
    2. 处理功能的组合关系 c. 行为
    3. 描述类和对象怎么交互和怎样分配职责
    4. 数据和数据处理分离
    1. 任何的代码都是 a) 数据结构 b) 算法逻辑
  5. 重点记忆 a. 创建型

    1. 工厂
    2. 建造者
    3. 单例 b. 结构型
    4. 适配器
    5. 装饰
    6. 享元
    7. 代理 c. 行为型
    8. 责任连
    9. 命令
    10. 观察者
    11. 策略
    12. 访问者
    13. 模板方法
  6. 工厂方法 a. 简单工厂

    1. 控制集中化,可以只维护一个地方的代码 b. 工厂方法的缺点
    2. 开闭原则 c. 解决问题的思路
    3. 工厂方法输出接口
  7. 建造者模式 a. 组合的过程配置化 b. 是一步一步创建一个复杂对象 c. 将一个复杂的对象构建与他的表示分离 d. 创建逻辑集中化

  8. 单例 a. 作用 1. 定义一个全局变量可以确保对象 2. 可以随时都可以被访问 3. 但不能防止我们的实例化多个对象 b. 单例的核心是 1. 配置核心化,配置集中 2. 解决系统控制粒度的核心化

  9. 适配器 a. 一个接口转换成另一个兼容接口 b. 使用场景 1. 第三方系统对接 2. 隔离外部变化时 c. 本质 1. 控制逻辑集中化、变化逻辑集中化 2. 就是一个变化的处理函数 3. 把变化的处理成使用不可变的数据 d. 引申 1. 生态系统稳定

  10. 代理模式 查一下代理模式的常用场景,具体优点

前端的设计模式系列-基本原则 | 前端的设计模式系列open in new windowPatterns.dev - Modern Web App Design Patternsopen in new window

Last Updated:
Contributors: cyio