设计模式分类

  • 创建型:单例、工厂、原型
  • 结构型:代理、装饰器、适配器
  • 行为型:观察者、策略、状态

一、单例模式

核心思想

保证一个类只有一个实例,并提供全局访问点

// 懒汉式单例
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance
    }
    Singleton.instance = this
  }
}
 
// 函数式单例
function createSingleton(name) {
  let instance = null
  return function () {
    if (!instance) {
      instance = { name }
    }
    return instance
  }
}
 
// 使用场景
// - Vuex/Pinia store
// - 全局配置对象
// - 日志对象

二、工厂模式

核心思想

将对象的创建与使用分离

// 简单工厂
class Animal {
  constructor(name) {
    this.name = name
  }
}
 
function createAnimal(type, name) {
  if (type === "dog") return new Dog(name)
  if (type === "cat") return new Cat(name)
  return new Animal(name)
}
 
// 工厂方法
class DogFactory {
  create(name) {
    return new Dog(name)
  }
}
 
// 使用场景
// - Vue 组件创建
// - 批量创建相似对象

三、原型模式

核心思想

通过克隆现有对象来创建新对象

// 原型链继承
const proto = {
  sayHello() {
    console.log("Hello")
  },
}
 
const obj1 = Object.create(proto)
const obj2 = Object.create(proto)
 
// 使用场景
// - 对象创建成本高
// - 需要保留原型的某些特性

四、观察者模式(发布订阅)

核心思想

定义对象间的一对多依赖,当一个对象改变时,所有依赖者都会收到通知

// 实现一个简单的 EventBus
class EventEmitter {
  constructor() {
    this.events = {}
  }
 
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(callback)
  }
 
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach((cb) => cb(data))
    }
  }
 
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter((cb) => cb !== callback)
    }
  }
}
 
// 使用场景
// - Vue 的 $emit/$on
// - Node.js EventEmitter
// - 组件间通信

五、策略模式

核心思想

定义一系列算法,把它们封装起来,使它们可以互相替换

// 传统写法
function calculatePrice(price, type) {
  if (type === "normal") return price
  if (type === "sale") return price * 0.8
  if (type === "half") return price * 0.5
}
 
// 策略模式
const strategies = {
  normal: (price) => price,
  sale: (price) => price * 0.8,
  half: (price) => price * 0.5,
}
 
function calculatePrice(price, type) {
  return strategies[type](price)
}
 
// 使用场景
// - 表单验证
// - 价格计算
// - 排序算法

六、代理模式

核心思想

为其他对象提供一种代理以控制对这个对象的访问

// 缓存代理
function createProxy(fn) {
  const cache = {}
  return function (...args) {
    const key = JSON.stringify(args)
    if (cache[key]) {
      console.log("命中缓存")
      return cache[key]
    }
    const result = fn(...args)
    cache[key] = result
    return result
  }
}
 
// Vue3 响应式代理
const obj = new Proxy(
  { name: "张三" },
  {
    get(target, key) {
      console.log(`访问 ${key}`)
      return target[key]
    },
    set(target, key, value) {
      console.log(`设置 ${key} = ${value}`)
      target[key] = value
      return true
    },
  },
)
 
// 使用场景
// - 缓存代理
// - 虚拟代理(图片懒加载)
// - 安全代理
// - Vue3 响应式

七、装饰器模式

核心思想

在不改变原对象的情况下,动态地给对象添加新功能

// 类装饰器
function readonly(target, key, descriptor) {
  descriptor.writable = false
  return descriptor
}
 
// 函数装饰器
function log(target, name, descriptor) {
  const original = descriptor.value
  descriptor.value = function (...args) {
    console.log(`Calling ${name} with`, args)
    return original.apply(this, args)
  }
  return descriptor
}
 
// 使用场景
// - React 高阶组件
// - 日志记录
// - 权限控制

八、适配器模式

核心思想

将一个类的接口转换成客户希望的另一个接口

// 电源适配器
class ChinaSocket {
  charge() {
    return "中国标准充电"
  }
}
 
class USAdapter {
  constructor() {
    this.socket = new ChinaSocket()
  }
  charge() {
    return this.socket.charge() // 转换接口
  }
}
 
// 使用场景
// - 接口兼容
// - 数据格式转换
// - Vue 组件封装

九、模式对比

模式目的特点
单例保证唯一实例全局唯一
工厂解耦创建与使用灵活创建
观察者一对多通知松耦合
策略算法可替换消除条件判断
代理控制访问增加中间层
装饰器动态添加功能不修改原对象
适配器接口转换兼容性处理

相关链接