Skip to content

JavaScript 设计模式面试题全解析

一、核心要点速览

💡 核心考点

  • 三大分类: 创建型、结构型、行为型
  • 高频模式: 单例、观察者、发布订阅、工厂、代理、策略
  • 实际应用: Vue/React 框架中的设计模式应用
  • 设计原则: SOLID、DRY、KISS、YAGNI

二、设计模式分类总览

1. 三大类型体系

JavaScript 设计模式分类架构图

创建型模式 (Creational):关注对象的创建过程,隐藏实例化逻辑

  • 单例模式、工厂模式、构造函数模式、原型模式、建造者模式

结构型模式 (Structural):关注类和对象的组合,简化结构设计

  • 代理模式、适配器模式、装饰器模式、组合模式、外观模式

行为型模式 (Behavioral):关注对象之间的通信和职责分配

  • 观察者模式、发布订阅模式、策略模式、迭代器模式、责任链模式

2. 设计模式选择指南

场景推荐模式解决问题
全局唯一实例单例模式避免重复创建
对象解耦通知观察者/发布订阅一对多依赖关系
复杂对象创建工厂模式封装创建逻辑
访问控制/增强代理模式拦截和增强功能
算法可替换策略模式消除条件分支
接口不兼容适配器模式转换接口格式
动态添加功能装饰器模式避免继承爆炸

三、创建型模式

1. 单例模式 (Singleton)

核心思想:保证一个类只有一个实例,并提供全局访问点。

最小实现

javascript
// ES6 Class 实现
class Singleton {
  constructor(name) {
    // 防止多次实例化
    if (Singleton.instance) {
      return Singleton.instance
    }
    
    this.name = name
    Singleton.instance = this
    return this
  }
  
  getName() {
    return this.name
  }
}

// 测试
const s1 = new Singleton('Alice')
const s2 = new Singleton('Bob')

console.log(s1 === s2)        // true
console.log(s1.getName())     // 'Alice'(第一次创建的实例)

闭包实现(更优雅)

javascript
const createSingleton = (() => {
  let instance = null
  
  return function(name) {
    if (!instance) {
      instance = {
        name,
        getName() {
          return this.name
        }
      }
    }
    return instance
  }
})()

// 使用
const s1 = createSingleton('Vue')
const s2 = createSingleton('React')

console.log(s1 === s2)  // true
console.log(s1.name)    // 'Vue'

实际应用场景

javascript
// ✓ 场景 1: 全局状态管理(Vuex/Pinia 核心)
class Store {
  constructor() {
    if (Store.instance) {
      return Store.instance
    }
    
    this.state = {}
    this.mutations = {}
    Store.instance = this
  }
  
  commit(type, payload) {
    if (this.mutations[type]) {
      this.mutations[type](this.state, payload)
    }
  }
}

const store = new Store()


// ✓ 场景 2: 日志管理器
class Logger {
  constructor() {
    if (Logger.instance) {
      return Logger.instance
    }
    
    this.logs = []
    Logger.instance = this
  }
  
  log(message) {
    this.logs.push({
      timestamp: Date.now(),
      message
    })
    console.log(`[LOG] ${message}`)
  }
  
  getLogs() {
    return this.logs
  }
}

const logger = new Logger()
logger.log('应用启动')
logger.log('用户登录')


// ✓ 场景 3: WebSocket 连接管理
class WebSocketManager {
  constructor(url) {
    if (WebSocketManager.instance) {
      return WebSocketManager.instance
    }
    
    this.ws = new WebSocket(url)
    this.listeners = []
    WebSocketManager.instance = this
  }
  
  send(data) {
    this.ws.send(JSON.stringify(data))
  }
  
  onMessage(callback) {
    this.listeners.push(callback)
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data)
      this.listeners.forEach(cb => cb(data))
    }
  }
}

2. 工厂模式 (Factory)

核心思想:定义一个创建对象的接口,让子类决定实例化哪个类。

简单工厂

javascript
// 产品类
class Button {
  render() {
    return '<button>按钮</button>'
  }
}

class Input {
  render() {
    return '<input type="text" />'
  }
}

class Select {
  render() {
    return '<select><option>选项</option></select>'
  }
}

// 工厂函数
function createComponent(type) {
  switch (type) {
    case 'button':
      return new Button()
    case 'input':
      return new Input()
    case 'select':
      return new Select()
    default:
      throw new Error(`未知组件类型: ${type}`)
  }
}

// 使用
const button = createComponent('button')
console.log(button.render())  // '<button>按钮</button>'

工厂方法模式

javascript
// 抽象工厂类
class ComponentFactory {
  createComponent() {
    throw new Error('子类必须实现此方法')
  }
  
  render() {
    const component = this.createComponent()
    return component.render()
  }
}

// 具体工厂
class ButtonFactory extends ComponentFactory {
  createComponent() {
    return new Button()
  }
}

class InputFactory extends ComponentFactory {
  createComponent() {
    return new Input()
  }
}

// 使用
const buttonFactory = new ButtonFactory()
console.log(buttonFactory.render())  // '<button>按钮</button>'

const inputFactory = new InputFactory()
console.log(inputFactory.render())   // '<input type="text" />'

实际应用场景

javascript
// ✓ 场景 1: HTTP 请求工厂
class HttpRequestFactory {
  static create(method, url, options = {}) {
    const config = {
      method: method.toUpperCase(),
      url,
      headers: { 'Content-Type': 'application/json' },
      ...options
    }
    
    switch (config.method) {
      case 'GET':
        return new GETRequest(config)
      case 'POST':
        return new POSTRequest(config)
      case 'PUT':
        return new PUTRequest(config)
      case 'DELETE':
        return new DELETERequest(config)
      default:
        throw new Error(`不支持的 HTTP 方法: ${config.method}`)
    }
  }
}

class GETRequest {
  constructor(config) {
    this.config = config
  }
  
  async execute() {
    const response = await fetch(this.config.url, {
      method: 'GET',
      headers: this.config.headers
    })
    return response.json()
  }
}

class POSTRequest {
  constructor(config) {
    this.config = config
  }
  
  async execute() {
    const response = await fetch(this.config.url, {
      method: 'POST',
      headers: this.config.headers,
      body: JSON.stringify(this.config.data)
    })
    return response.json()
  }
}

// 使用
const request = HttpRequestFactory.create('POST', '/api/users', {
  data: { name: 'Vue', age: 3 }
})
request.execute()


// ✓ 场景 2: 表单验证器工厂
class ValidatorFactory {
  static create(type, rule) {
    switch (type) {
      case 'required':
        return new RequiredValidator(rule)
      case 'email':
        return new EmailValidator(rule)
      case 'minLength':
        return new MinLengthValidator(rule)
      case 'pattern':
        return new PatternValidator(rule)
      default:
        throw new Error(`未知验证器类型: ${type}`)
    }
  }
}

class RequiredValidator {
  validate(value) {
    return value ? { valid: true } : { valid: false, message: '必填项' }
  }
}

class EmailValidator {
  validate(value) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return emailRegex.test(value) 
      ? { valid: true } 
      : { valid: false, message: '邮箱格式错误' }
  }
}

四、行为型模式

1. 观察者模式 (Observer)

核心思想:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖者都会收到通知。

最小实现

javascript
class Subject {
  constructor() {
    this.observers = []
  }
  
  // 订阅
  subscribe(observer) {
    this.observers.push(observer)
  }
  
  // 取消订阅
  unsubscribe(observer) {
    this.observers = this.observers.filter(obs => obs !== observer)
  }
  
  // 通知所有观察者
  notify(data) {
    this.observers.forEach(observer => observer.update(data))
  }
}

class Observer {
  constructor(name) {
    this.name = name
  }
  
  update(data) {
    console.log(`${this.name} 收到通知:`, data)
  }
}

// 使用
const subject = new Subject()

const observer1 = new Observer('观察者1')
const observer2 = new Observer('观察者2')

subject.subscribe(observer1)
subject.subscribe(observer2)

subject.notify('状态改变')
// 输出:
// 观察者1 收到通知: 状态改变
// 观察者2 收到通知: 状态改变

subject.unsubscribe(observer1)
subject.notify('再次改变')
// 输出: 观察者2 收到通知: 再次改变

Vue 响应式原理中的应用

javascript
// Vue 2 响应式系统简化版
class Dep {
  constructor() {
    this.subs = []
  }
  
  // 收集依赖
  depend() {
    if (Dep.target && !this.subs.includes(Dep.target)) {
      this.subs.push(Dep.target)
    }
  }
  
  // 通知更新
  notify() {
    this.subs.forEach(sub => sub.update())
  }
}

class Watcher {
  constructor(vm, key, callback) {
    this.vm = vm
    this.key = key
    this.callback = callback
    this.value = this.get()
  }
  
  get() {
    Dep.target = this
    const value = this.vm[this.key]
    Dep.target = null
    return value
  }
  
  update() {
    const newValue = this.vm[this.key]
    if (newValue !== this.value) {
      this.value = newValue
      this.callback(newValue)
    }
  }
}

// 模拟 Vue 实例
const vm = {
  message: 'Hello',
  _dep: new Dep()
}

// 劫持属性
Object.defineProperty(vm, 'message', {
  get() {
    this._dep.depend()
    return this._message
  },
  set(newVal) {
    this._message = newVal
    this._dep.notify()
  }
})

vm._message = 'Hello'

// 创建观察者
new Watcher(vm, 'message', (newVal) => {
  console.log('视图更新:', newVal)
})

vm.message = 'World'  // 触发更新: 视图更新: World

2. 发布订阅模式 (Pub/Sub)

核心思想:通过事件中心解耦发布者和订阅者,两者互不认识。

与观察者模式的区别

观察者模式 vs 发布订阅模式对比图

关键区别总结

维度观察者模式发布订阅模式
耦合度紧耦合(直接通知)松耦合(通过事件总线)
中介有事件总线(Event Bus)
灵活性较低较高
性能较高(少一层中转)稍低(多一层中转)
适用场景紧密关联的对象松散耦合的组件

最小实现

javascript
class EventBus {
  constructor() {
    this.events = {}
  }
  
  // 订阅事件
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(callback)
    
    // 返回取消订阅函数
    return () => this.off(event, callback)
  }
  
  // 一次性订阅
  once(event, callback) {
    const wrapper = (...args) => {
      callback(...args)
      this.off(event, wrapper)
    }
    return this.on(event, wrapper)
  }
  
  // 发布事件
  emit(event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach(callback => {
        try {
          callback(...args)
        } catch (error) {
          console.error(`事件 ${event} 回调执行失败:`, error)
        }
      })
    }
  }
  
  // 取消订阅
  off(event, callback) {
    if (this.events[event]) {
      if (callback) {
        this.events[event] = this.events[event].filter(cb => cb !== callback)
      } else {
        delete this.events[event]
      }
    }
  }
}

// 使用
const bus = new EventBus()

// 订阅
const unsubscribe = bus.on('user:login', (user) => {
  console.log(`用户 ${user.name} 登录`)
})

bus.on('user:logout', (user) => {
  console.log(`用户 ${user.name} 登出`)
})

// 发布
bus.emit('user:login', { name: 'Vue' })
// 输出: 用户 Vue 登录

// 取消订阅
unsubscribe()
bus.emit('user:login', { name: 'React' })
// 不会输出(已取消订阅)

实际应用场景

javascript
// ✓ 场景 1: 组件通信(Vue/React 事件总线)
class ComponentEventBus {
  constructor() {
    this.bus = new EventBus()
  }
  
  // 组件 A 发送消息
  sendMessage(channel, data) {
    this.bus.emit(channel, data)
  }
  
  // 组件 B 接收消息
  receiveMessage(channel, handler) {
    return this.bus.on(channel, handler)
  }
}

// 使用
const eventBus = new ComponentEventBus()

// 组件 A
eventBus.sendMessage('cart:update', { productId: 123, quantity: 2 })

// 组件 B
eventBus.receiveMessage('cart:update', (data) => {
  console.log('购物车更新:', data)
})


// ✓ 场景 2: 任务调度系统
class TaskScheduler {
  constructor() {
    this.bus = new EventBus()
  }
  
  // 注册任务
  registerTask(taskName, handler) {
    return this.bus.on(`task:${taskName}`, handler)
  }
  
  // 触发任务
  triggerTask(taskName, data) {
    console.log(`触发任务: ${taskName}`)
    this.bus.emit(`task:${taskName}`, data)
  }
  
  // 任务完成后通知
  onTaskComplete(taskName, callback) {
    return this.bus.on(`task:${taskName}:complete`, callback)
  }
}

// 使用
const scheduler = new TaskScheduler()

// 注册数据处理任务
scheduler.registerTask('data-process', (data) => {
  console.log('处理数据:', data)
  // 模拟异步处理
  setTimeout(() => {
    scheduler.bus.emit('task:data-process:complete', { result: 'success' })
  }, 1000)
})

// 监听任务完成
scheduler.onTaskComplete('data-process', (result) => {
  console.log('任务完成:', result)
})

// 触发任务
scheduler.triggerTask('data-process', { source: 'API' })

3. 策略模式 (Strategy)

核心思想:定义一系列算法,将每个算法封装起来,并使它们可以互换。

最小实现

javascript
// 策略类
class DiscountStrategy {
  calculate(price) {
    throw new Error('子类必须实现此方法')
  }
}

class NoDiscount extends DiscountStrategy {
  calculate(price) {
    return price
  }
}

class PercentageDiscount extends DiscountStrategy {
  constructor(percentage) {
    super()
    this.percentage = percentage
  }
  
  calculate(price) {
    return price * (1 - this.percentage)
  }
}

class FixedDiscount extends DiscountStrategy {
  constructor(amount) {
    super()
    this.amount = amount
  }
  
  calculate(price) {
    return Math.max(0, price - this.amount)
  }
}

// 上下文类
class PriceCalculator {
  constructor(strategy) {
    this.strategy = strategy
  }
  
  // 切换策略
  setStrategy(strategy) {
    this.strategy = strategy
  }
  
  // 计算价格
  calculatePrice(price) {
    return this.strategy.calculate(price)
  }
}

// 使用
const calculator = new PriceCalculator(new NoDiscount())
console.log(calculator.calculatePrice(100))  // 100

calculator.setStrategy(new PercentageDiscount(0.2))
console.log(calculator.calculatePrice(100))  // 80

calculator.setStrategy(new FixedDiscount(15))
console.log(calculator.calculatePrice(100))  // 85

实际应用场景

javascript
// ✓ 场景 1: 表单验证策略
const validationStrategies = {
  required: (value) => {
    return value ? null : '此项为必填项'
  },
  email: (value) => {
    const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return pattern.test(value) ? null : '邮箱格式不正确'
  },
  minLength: (value, min) => {
    return value.length >= min ? null : `最少需要${min}个字符`
  },
  maxLength: (value, max) => {
    return value.length <= max ? null : `最多允许${max}个字符`
  },
  pattern: (value, regex) => {
    return regex.test(value) ? null : '格式不符合要求'
  }
}

class FormValidator {
  constructor() {
    this.rules = {}
  }
  
  // 添加验证规则
  addRule(field, strategy, ...params) {
    if (!this.rules[field]) {
      this.rules[field] = []
    }
    this.rules[field].push({ strategy, params })
  }
  
  // 验证字段
  validateField(field, value) {
    const rules = this.rules[field]
    if (!rules) return null
    
    for (const { strategy, params } of rules) {
      const error = validationStrategies[strategy](value, ...params)
      if (error) return error
    }
    return null
  }
  
  // 验证整个表单
  validate(formData) {
    const errors = {}
    
    for (const field in this.rules) {
      const error = this.validateField(field, formData[field])
      if (error) {
        errors[field] = error
      }
    }
    
    return Object.keys(errors).length === 0 ? null : errors
  }
}

// 使用
const validator = new FormValidator()
validator.addRule('username', 'required')
validator.addRule('username', 'minLength', 3)
validator.addRule('email', 'required')
validator.addRule('email', 'email')

const errors = validator.validate({
  username: 'ab',
  email: 'invalid-email'
})

console.log(errors)
// { username: '最少需要3个字符', email: '邮箱格式不正确' }


// ✓ 场景 2: 支付策略
const paymentStrategies = {
  alipay: {
    pay(amount) {
      console.log(`使用支付宝支付 ¥${amount}`)
      return { success: true, method: 'alipay' }
    }
  },
  wechat: {
    pay(amount) {
      console.log(`使用微信支付 ¥${amount}`)
      return { success: true, method: 'wechat' }
    }
  },
  creditCard: {
    pay(amount) {
      console.log(`使用信用卡支付 ¥${amount}`)
      return { success: true, method: 'creditCard' }
    }
  }
}

class PaymentProcessor {
  constructor() {
    this.strategy = null
  }
  
  setStrategy(method) {
    this.strategy = paymentStrategies[method]
    if (!this.strategy) {
      throw new Error(`不支持的支付方式: ${method}`)
    }
  }
  
  process(amount) {
    if (!this.strategy) {
      throw new Error('未设置支付方式')
    }
    return this.strategy.pay(amount)
  }
}

// 使用
const processor = new PaymentProcessor()
processor.setStrategy('alipay')
processor.process(100)  // 使用支付宝支付 ¥100

processor.setStrategy('wechat')
processor.process(200)  // 使用微信支付 ¥200

五、结构型模式

1. 代理模式 (Proxy)

核心思想:为其他对象提供一种代理以控制对这个对象的访问。

最小实现

javascript
// 目标对象
const target = {
  name: 'Vue',
  age: 3
}

// 代理对象
const handler = {
  get(target, property) {
    console.log(`访问属性: ${property}`)
    return Reflect.get(target, property)
  },
  
  set(target, property, value) {
    console.log(`设置属性: ${property} = ${value}`)
    return Reflect.set(target, property, value)
  }
}

const proxy = new Proxy(target, handler)

// 使用
proxy.name        // 访问属性: name → 'Vue'
proxy.age = 4     // 设置属性: age = 4

实际应用场景

javascript
// ✓ 场景 1: 数据验证代理
function createValidatedObject(obj, validators) {
  return new Proxy(obj, {
    set(target, property, value) {
      const validator = validators[property]
      
      if (validator) {
        const error = validator(value)
        if (error) {
          throw new Error(`属性 ${property} 验证失败: ${error}`)
        }
      }
      
      return Reflect.set(target, property, value)
    }
  })
}

// 使用
const user = createValidatedObject(
  { name: '', age: 0 },
  {
    name: (value) => {
      if (!value || value.length < 2) {
        return '姓名至少2个字符'
      }
      return null
    },
    age: (value) => {
      if (!Number.isInteger(value) || value < 0 || value > 150) {
        return '年龄必须是0-150的整数'
      }
      return null
    }
  }
)

user.name = 'Vue'      // ✓ 成功
user.age = 3           // ✓ 成功
user.name = 'V'        // ✗ 抛出错误: 姓名至少2个字符


// ✓ 场景 2: 缓存代理
function createCachedFunction(fn) {
  const cache = new Map()
  
  return new Proxy(fn, {
    apply(target, thisArg, args) {
      const key = JSON.stringify(args)
      
      if (cache.has(key)) {
        console.log('从缓存读取')
        return cache.get(key)
      }
      
      const result = target.apply(thisArg, args)
      cache.set(key, result)
      console.log('计算并缓存')
      return result
    }
  })
}

// 使用
const expensiveCalculation = (n) => {
  console.log('执行耗时计算...')
  return n * n
}

const cachedCalc = createCachedFunction(expensiveCalculation)

cachedCalc(5)   // 执行耗时计算... → 25
cachedCalc(5)   // 从缓存读取 → 25
cachedCalc(10)  // 执行耗时计算... → 100


// ✓ 场景 3: API 请求代理
function createApiProxy(baseUrl) {
  return new Proxy({}, {
    get(target, property) {
      return async (...args) => {
        const url = `${baseUrl}/${property}`
        const [params] = args
        
        console.log(`发起请求: ${url}`, params)
        
        try {
          const response = await fetch(url, {
            method: params?.method || 'GET',
            headers: { 'Content-Type': 'application/json' },
            body: params?.body ? JSON.stringify(params.body) : undefined
          })
          
          if (!response.ok) {
            throw new Error(`HTTP ${response.status}`)
          }
          
          return await response.json()
        } catch (error) {
          console.error(`请求失败: ${url}`, error)
          throw error
        }
      }
    }
  })
}

// 使用
const api = createApiProxy('/api')

// 自动转换为 API 调用
api.users()                    // GET /api/users
api.users({ id: 123 })         // GET /api/users?id=123
api.posts({                    // POST /api/posts
  method: 'POST',
  body: { title: 'Hello' }
})

2. 装饰器模式 (Decorator)

核心思想:在不改变原对象的基础上,动态地给对象添加新的功能。

最小实现

javascript
// 基础类
class Coffee {
  cost() {
    return 10
  }
  
  description() {
    return '普通咖啡'
  }
}

// 装饰器基类
class CoffeeDecorator {
  constructor(coffee) {
    this.coffee = coffee
  }
  
  cost() {
    return this.coffee.cost()
  }
  
  description() {
    return this.coffee.description()
  }
}

// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
  cost() {
    return super.cost() + 5
  }
  
  description() {
    return super.description() + ' + 牛奶'
  }
}

class SugarDecorator extends CoffeeDecorator {
  cost() {
    return super.cost() + 2
  }
  
  description() {
    return super.description() + ' + 糖'
  }
}

// 使用
let coffee = new Coffee()
console.log(coffee.description())  // '普通咖啡'
console.log(coffee.cost())         // 10

coffee = new MilkDecorator(coffee)
console.log(coffee.description())  // '普通咖啡 + 牛奶'
console.log(coffee.cost())         // 15

coffee = new SugarDecorator(coffee)
console.log(coffee.description())  // '普通咖啡 + 牛奶 + 糖'
console.log(coffee.cost())         // 17

函数装饰器

javascript
// 日志装饰器
function withLogging(fn) {
  return function(...args) {
    console.log(`调用 ${fn.name},参数:`, args)
    const result = fn.apply(this, args)
    console.log(`${fn.name} 返回:`, result)
    return result
  }
}

// 性能监控装饰器
function withPerformance(fn) {
  return function(...args) {
    const start = performance.now()
    const result = fn.apply(this, args)
    const end = performance.now()
    console.log(`${fn.name} 执行时间: ${(end - start).toFixed(2)}ms`)
    return result
  }
}

// 错误处理装饰器
function withErrorHandling(fn) {
  return function(...args) {
    try {
      return fn.apply(this, args)
    } catch (error) {
      console.error(`${fn.name} 执行出错:`, error)
      return null
    }
  }
}

// 使用
function add(a, b) {
  return a + b
}

const loggedAdd = withLogging(add)
loggedAdd(2, 3)
// 输出:
// 调用 add,参数: [2, 3]
// add 返回: 5

const perfAdd = withPerformance(add)
perfAdd(2, 3)
// 输出: add 执行时间: 0.05ms

const safeAdd = withErrorHandling(add)
safeAdd(2, 3)  // 5
safeAdd('2', null)  // 不会抛出错误,返回 null

六、设计原则

1. SOLID 原则

┌──────────────────────────────────────────────────────────┐
│                  SOLID 设计原则                           │
└──────────────────────────────────────────────────────────┘

S - 单一职责原则 (Single Responsibility)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
一个类应该只有一个引起它变化的原因

✓ 好的做法:
class UserService {
  createUser(user) { /* ... */ }
  getUser(id) { /* ... */ }
}

class EmailService {
  sendEmail(to, content) { /* ... */ }
}

✗ 坏的做法:
class UserService {
  createUser(user) { /* ... */ }
  sendWelcomeEmail(user) { /* ... */ }  // 违反 SRP
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

O - 开闭原则 (Open/Closed)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
对扩展开放,对修改关闭

✓ 好的做法:
class Shape {
  area() { throw new Error('必须实现') }
}

class Circle extends Shape {
  constructor(radius) { super(); this.radius = radius }
  area() { return Math.PI * this.radius ** 2 }
}

class Rectangle extends Shape {
  constructor(w, h) { super(); this.w = w; this.h = h }
  area() { return this.w * this.h }
}

✗ 坏的做法:
function calculateArea(shape, type) {
  if (type === 'circle') { /* ... */ }
  if (type === 'rectangle') { /* ... */ }  // 需要修改代码
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

L - 里氏替换原则 (Liskov Substitution)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
子类应该能够替换父类

✓ 好的做法:
class Bird {
  fly() { /* ... */ }
}

class Sparrow extends Bird {
  fly() { /* 麻雀会飞 */ }
}

✗ 坏的做法:
class Bird {
  fly() { /* ... */ }
}

class Penguin extends Bird {
  fly() { throw new Error('企鹅不会飞') }  // 违反 LSP
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

I - 接口隔离原则 (Interface Segregation)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
客户端不应该依赖它不需要的接口

✓ 好的做法:
interface Printable {
  print()
}

interface Scannable {
  scan()
}

class Printer implements Printable {
  print() { /* ... */ }
}

class Scanner implements Scannable {
  scan() { /* ... */ }
}

✗ 坏的做法:
interface Machine {
  print()
  scan()
}

class Printer implements Machine {
  print() { /* ... */ }
  scan() { throw new Error('不支持') }  // 违反 ISP
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

D - 依赖倒置原则 (Dependency Inversion)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
高层模块不应该依赖低层模块,两者都应该依赖抽象

✓ 好的做法:
class Database {
  connect() { throw new Error('必须实现') }
}

class MySQLDatabase extends Database {
  connect() { /* MySQL 连接 */ }
}

class UserService {
  constructor(database) {
    this.db = database  // 依赖抽象
  }
}

const service = new UserService(new MySQLDatabase())

✗ 坏的做法:
class UserService {
  constructor() {
    this.db = new MySQLDatabase()  // 依赖具体实现
  }
}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

2. 其他重要原则

原则缩写含义
DRYDon't Repeat Yourself不要重复代码
KISSKeep It Simple, Stupid保持简单
YAGNIYou Aren't Gonna Need It不要过度设计
LODLaw of Demeter迪米特法则(最少知识原则)

七、框架中的设计模式

1. Vue 中的设计模式

javascript
// ✓ 观察者模式: 响应式系统
// Dep 收集依赖,Watcher 观察变化

// ✓ 发布订阅模式: 事件总线
// vm.$on / vm.$emit

// ✓ 代理模式: Vue 3 响应式
// const reactive = new Proxy(obj, handler)

// ✓ 组合模式: 组件树
// 父子组件嵌套形成树形结构

// ✓ 工厂模式: 创建组件实例
// Vue.component('MyComponent', {...})

2. React 中的设计模式

javascript
// ✓ 观察者模式: useState/useEffect
// 状态变化触发重新渲染

// ✓ 组合模式: 组件组合
// <App><Header /><Main /></App>

// ✓ 高阶组件 (HOC): 装饰器模式
// const Enhanced = withLogging(Component)

// ✓ 策略模式: 自定义 Hooks
// 不同的 Hook 提供不同的行为

八、面试标准回答

设计模式是解决常见软件设计问题的最佳实践,我将其分为三类:

创建型模式关注对象的创建:

  • 单例模式:保证全局唯一实例,常用于全局状态管理、日志器等
  • 工厂模式:封装对象创建逻辑,根据条件返回不同实例

行为型模式关注对象间的交互:

  • 观察者模式:主题和观察者直接耦合,一对多通知
  • 发布订阅模式:通过事件中心解耦,发布者和订阅者互不认识
  • 策略模式:封装可互换的算法族,消除条件分支

结构型模式关注类的组合:

  • 代理模式:控制对象访问,可用于缓存、验证、日志等
  • 装饰器模式:动态添加功能,不修改原对象

实际应用中:

  • Vue 响应式系统使用了观察者模式和代理模式
  • React 的状态管理使用了观察者模式
  • 事件总线使用了发布订阅模式
  • 表单验证、支付场景常用策略模式

设计原则方面,我遵循 SOLID 原则:

  • 单一职责:一个类只做一件事
  • 开闭原则:对扩展开放,对修改关闭
  • 依赖倒置:依赖抽象而非具体实现

我的经验是不要过度设计,先写出可读的代码,发现重复模式后再重构。


九、记忆口诀

设计模式歌诀:

三大类型要分清,
创建结构加行为。

单例保证唯一性,
工厂封装创建逻辑。

观察者是一对多,
发布订阅靠中介。

策略模式换算法,
代理控制访问权。

装饰器来添功能,
不改原物最聪明。

SOLID 原则记心间,
单一职责开闭先。
里氏替换接口隔,
依赖倒置抽象连!

设计模式非教条,
灵活运用最关键。
过度设计要避免,
简洁清晰是第一!

十、推荐资源


十一、总结一句话

  • 创建型: 单例唯一 + 工厂封装 = 灵活创建对象 🏭
  • 行为型: 观察订阅 + 策略互换 = 解耦通信逻辑 📡
  • 结构型: 代理控制 + 装饰增强 = 优雅扩展功能 🎨
  • 设计原则: SOLID + DRY/KISS = 高质量代码基石
最近更新