个人的博客
个人的博客

常见设计模式

总结一些前端项目中常见的设计模式

# 一、工厂模式

工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,用工厂方法代替new操作的一种模式。

特征:
1、构造函数和创建者分离,对new操作进行封装
2、符合开放封闭原则

// 举个栗子
// 食品实例生成的详细过程
class Food {
  constructor(name, type) {
    this.name = name // 食品
    this.type = type // 食品类型
  }
  desc() {
    console.log(`我是${this.name},属于${this.type}类型`);
  }
}

class FoodFactory {
  // ES6中静态方法不会被实例调用,可以被类调用
  static create(name, type) {
    // 创建一种食品实例
    return new Food(name, type)
  }
}

// 工厂开始生产食品实例了
FoodFactory.create('核桃', '坚果').desc() // 我是核桃, 属于坚果类型
FoodFactory.create('旺仔', '饮品').desc() // 我是旺仔, 属于饮品类型
// 这样我们就可以 生成出我们想要的食品, 只要你给我们 一个名称和类型

# 二、单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

特征: 1、单例模式的主要思想就是,实例如果已经创建,则直接返回

class Singleton {
  // js模拟
  constructor() {}
  login() {
    console.log('login...');
  }
}

// 给类挂载一个静态方法, 只有一个
// 自执行函数: 只是为了将变量保存在 函数的作用域中, 避免污染而已
Singleton.singleInstance = (function () {
  // 通过闭包实现: 类似通过一个全局变量来存储这个实例
  let instance;  // 保存创建好的实例
  return function () {
    if (!instance) {
      // 如果没有创建, 就创建一个
      instance = new Singleton()
    }
    return instance
  }
})()
// 通过调用静态方法来创建单实例
let single11 = Singleton.singleInstance()
let single22 = Singleton.singleInstance()
// 通过调用类 初始化的实例
let single33 = new Singleton()
console.log(single11 === single22);  // ture
console.log(single11 === single33); // false

# 三、观察者模式/发布订阅模式

# 1、观察者模式

观察者模式 其实就是一个目标对象(Subject),维护一系列依赖与他的对象(Obsever),当目标对象(Subject)状态发生改变自动通知它们(Oberver)。

模式逻辑入下图:

观察者模式逻辑图

代码实现:

// 目标对象
class Subject{
  constructor(){
    // 观察者列表
    this.observers = []
  }
  // 添加观察者
  addObserver(observer){
    this.observers.push(observer)
  }
  // 删除观察者
  removeObserver(observer){
    let index = this.observers.findIndex(o => o === observer)
    if(o != -1){
      this.observers.splice(index,1)
      return true
    }
    return false
  }
  // 通知
  notify(msg){
    this.observers.forEach(o=>o.update())
  }
}

// 观察者
class Observer{
  constructor(name){
    this.name = name
  }
  // 自定义的更新方法
  update(){
    console.log(this.name + '执行更新操作!')
  }
}

// 创建目标对象、观察者
let sub = new Subject()
let ob1 = new Observer('观察者1')
let ob2 = new Observer('观察者2')

// 订阅 观察者 
sub.addObserver(ob1)
sub.addObserver(ob2)

// 目标发布通知
sub.notify()

// 删除 观察者订阅
sub.removeObserver(ob1)

// 再次发布通知
sub.notify()

# 2、发布订阅模式

发布-订阅模式,就是一个发布者对象(Publisher)通过一个事件通道(Event Channel)发布指定的事件,通知多个订阅该事件的订阅者对象(Subscriber)。

模式逻辑入下图:

发布-订阅模式逻辑图

代码实现:

// 发布-订阅类
class PublishSubscribe{
  constructor(){
    // 事件列表
    this._events = {} 
  }
  // 发布事件
  $emit(topic, arg){
    if(this._events[topic]){
      this._events[topic].forEach(fn => fn(arg))
    }
  }
  // 订阅
  $on(topic, fn){
    if(this._events[topic]){
      this._events[topic].push(fn)
    }else{
       this._events[topic] = [fn]
    }
  }
  // 取消订阅
  $off(topic,fn){
    if(this._events[topic]){
      if(fn){
        let index = this._events[topic].findIndex(e => e === fn)
        if(index != -1){
          this._events[topic].splice(index,1)
        }
      }else{
        delete this._events[topic]
      }
    }
  }
}

// 创建发布-订阅实例
let pubSub = new PublishSubscribe()
// 订阅 'log' 事件
pubSub.$on('log',(msg)=>{
  console.log('log1',msg)
})
pubSub.$on('log',(msg)=>{
  console.log('log2',msg)
})
function consoleLog(msg){
  console.log('log3',msg)
}
pubSub.$on('log',consoleLog)

// 订阅 error 事件
pubSub.$on('error',(msg)=>{
  console.log('error',msg)
})

// 发布事件
pubSub.$emit('log','输出日志')
pubSub.$emit('error','报错啦')
// 控制台打印:
// log1 输出日志
// log2 输出日志
// log3 输出日志
// error 报错啦

// 取消单个订阅
pubSub.$off('log', consoleLog)

// 再次发布事件
pubSub.$emit('log','再次输出日志')
// 控制台打印:
// log1 输出日志
// log2 输出日志

// 取消指定类型的所有订阅
pubSub.$off('log')

// 再次发布事件
pubSub.$emit('log','输出日志') // 无打印内容

# 3、两种模式之间的差异

  • Observer模式,观察者直接订阅内容改变的事件,定义一个对多的依赖关系
  • Publish/Subscribe模式,通过一个事件通道关联发布者和订阅者,发布者与订阅者不存在直接依赖关系。