设计模式

46 阅读2分钟

工厂模式

什么是工厂模式

// 产品类:汽车
class Car {
  private model;
  private engine;

  constructor(model: string, engine: string) {
    this.model = model;
    this.engine = engine;
  }

  drive() {
    console.log(`Driving ${this.model} with ${this.engine} engine`);
  }
}

function createCar(type: string) {
  switch (type) {
    case "sports":
      return new Car("Sports Car", "V8");
    case "sedan":
      return new Car("Sedan", "V6");
    default:
      throw new Error("Unknown car type");
  }
}

// 使用
const sportsCar = createCar("sports");
sportsCar.drive(); // Driving Sports Car with V8 engine

const sedanCar = createCar("sedan");
sedanCar.drive(); // Driving Sports Car with V8 engine

应用

JQuery$函数

class JQuery {}

windown.$ = function() {
    return new JQuery()
}

ReactcreateElement

class Vdom {}

function createElement() {
    return new Vdom()
}

单例模式

什么是单例模式

  • 全局唯一的实力
  • Vuex redux 的 store
  • 全局唯一的弹框 dialog modal
// 产品类:汽车
class Car {
  static instance: Car;

  // 如果给constructor加上private, 外部就无法new了
  // private constructor() {}

  static createCar() {
    if (!Car.instance) {
      Car.instance = new Car();
    }

    return Car.instance;
  }
}

// 使用
const car1 = Car.createCar();
const car2 = Car.createCar();
const car3 = new Car();
const car4 = new Car();

console.log(car1 === car2, car3 === car4); // true false

应用: 弹框

code

class Dialog {

  static instance // 唯一实例
  private overlay // 遮罩
  private modal // 弹框
  private content // 装文字的容器

  constructor() {
    this.overlay = document.createElement('div');
    this.overlay.id = 'overlay';
    
    this.modal = document.createElement('div');
    this.modal.id = 'modal';
    this.modal.innerHTML = 
      `
      <div class="content"></div>
      <button onclick="Dialog.instance.hide()">关闭</button>
      `;
    
    this.content = this.modal.querySelector('.content')

    document.body.append(this.overlay, this.modal);
  }

  private show(text: string) {
    Dialog.instance.content.innerHTML = text;
    Dialog.instance.modal.style.display = 'block';
    Dialog.instance.overlay.style.display = 'block';
  }

  private hide() {
    if (!Dialog.instance) return;
    Dialog.instance.modal.style.display = 'none';
    Dialog.instance.overlay.style.display = 'none';
  }

  static Modal() {
    if(!Dialog.instance) {
      Dialog.instance = new Dialog()
    }

    return Dialog.instance
  }
}

const modal = Dialog.Modal()
const btn = document.querySelector('#btn')

btn.addEventListener('click', () => {
  modal.show('大展宏图')
})

发布订阅模式

什么是发布订阅模式

function f1() {}
function f2() {}

// 绑定
e.on('e-key1', f1)
e.on('e-key2', f2)

// 触发
e.emit('e-key1')

// 解绑
e.off('e-key1', f1)
e.off('e-key2', f2)

EventBus

type TFn = (data?: unknown) => void;

interface IEvents {
  [key: string]: TFn[];
}

class EventBus {
  events: IEvents = {};

  on(key: string, fn: TFn) {
    if(!this.events[key]) this.events[key] = []
    this.events[key].push(fn);
  }

  emit(key: string, data?: unknown) {
    (this.events[key] || []).forEach((fn) => fn(data));
  }

  off(key: string, fn: TFn) {
    this.events[key] = this.events[key] || [];
    const index = this.events[key].indexOf(fn);
    if (index === -1) return;
    this.events[key].splice(index, 1);
  }
}

export default EventBus;

观察者模式

  • 就是监听

例1

btn.addEventListener('click', () => { ... })

例2

class Subject {
  constructor() {
    this.observers = [];
  }
  
  subscribe(fn) {
    this.observers.push(fn);
  }
  
  unsubscribe(fn) {
    this.observers = this.observers.filter(obs => obs !== fn);
  }
  
  notify(data) {
    this.observers.forEach(fn => fn(data));
  }
}

// 使用函数作为观察者
const subject = new Subject();

subject.subscribe(data => console.log(`日志: ${data}`));
subject.subscribe(data => console.log(`警报: ${data} 发生!`));
subject.notify("温度过高"); 
// 输出:
// 日志: 温度过高
// 警报: 温度过高 发生!

代理模式

  • 使用者不能直接访问对象, 而是访问一个代理层
  • 代理层可以监听 get, set
  • 如ES6 Proxy 实现vue3响应式

const o = {
  a: 1,
  b: 2,
}

const oo = new Proxy(o, {
  get(target, key) {
    console.log('读取了: ', key)
    return target[key]
  },
  set(target, key, value) {
    console.log('修改了: ', key, '->', value)
    target[key] = value
    return true // 赋值成功返回true, 赋值失败返回false
  }
})

oo.a
oo.a = 11

装饰器模式

  • AOP面向切片编程
  • 应用: nestjs

type Constructor = new (...arg: any[]) => any

const name = (name: string) => (constructor: Constructor) => class extends constructor {
  name = name
}

@name('tom')
class A {

}
console.log(new A()); // {name: 'tom'}