阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 常见设计模式误用与反模式

常见设计模式误用与反模式

作者:陈川 阅读数:12096人阅读 分类: JavaScript

设计模式误用的典型场景

设计模式本为解决特定问题而生,但机械套用往往适得其反。Singleton模式在JavaScript中的滥用尤为常见,开发者常将模块全局化当作单例实现:

// 反模式示例:伪单例
const pseudoSingleton = {
  data: [],
  add(item) {
    this.data.push(item)
  }
}

// 模块A修改数据
pseudoSingleton.add('A')

// 模块B意外覆盖数据
pseudoSingleton.data = null

这种实现缺乏真正的实例控制,无法阻止直接修改内部状态。更合理的做法应使用闭包保护实例:

const RealSingleton = (() => {
  let instance
  
  class Singleton {
    constructor() {
      this.data = []
    }
    add(item) {
      this.data.push(item)
    }
  }

  return {
    getInstance() {
      if (!instance) {
        instance = new Singleton()
      }
      return instance
    }
  }
})()

观察者模式的过度订阅陷阱

事件驱动架构中,观察者模式常因未及时取消订阅导致内存泄漏。典型场景如SPA中的全局事件总线:

// 危险实现
class EventBus {
  listeners = {}

  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = []
    }
    this.listeners[event].push(callback)
  }

  emit(event, data) {
    (this.listeners[event] || []).forEach(cb => cb(data))
  }
}

// 组件内使用
mounted() {
  eventBus.on('dataUpdate', this.handleData)
}

// 组件销毁时忘记取消订阅 -> 内存泄漏

改进方案应强制要求提供取消订阅机制:

class SafeEventBus {
  listeners = new Map()

  on(event, callback) {
    const callbacks = this.listeners.get(event) || new Set()
    callbacks.add(callback)
    this.listeners.set(event, callbacks)
    return () => this.off(event, callback) // 返回取消函数
  }

  off(event, callback) {
    const callbacks = this.listeners.get(event)
    if (callbacks) {
      callbacks.delete(callback)
    }
  }
}

策略模式的条件爆炸

策略模式本应简化条件分支,但不当实现会导致策略类膨胀:

// 反模式:策略类包含过多条件
class PaymentStrategy {
  pay(method, amount) {
    switch(method) {
      case 'alipay':
        return this.processAlipay(amount)
      case 'wechat':
        return this.processWeChat(amount)
      case 'creditCard':
        return this.processCreditCard(amount)
      // 新增支付方式需要修改类
    }
  }
}

应拆分为独立策略对象:

const strategies = {
  alipay: (amount) => { /* 具体实现 */ },
  wechat: (amount) => { /* 具体实现 */ },
  creditCard: (amount) => { /* 具体实现 */ }
}

function processPayment(method, amount) {
  return strategies[method]?.(amount) ?? defaultStrategy(amount)
}

装饰器模式的性能代价

装饰器模式滥用会导致调用栈过深,特别是在React高阶组件中:

// 多层装饰导致组件树难以调试
const EnhancedComponent = withRouter(
  connect(mapStateToProps)(
    withStyles(styles)(
      memo(BaseComponent)
    )
  )
)

现代React更推荐使用Hook组合:

function SmartComponent() {
  const router = useRouter()
  const data = useSelector(mapStateToProps)
  const classes = useStyles(styles)
  return <BaseComponent {...props} />
}

工厂方法的过度抽象

工厂方法在简单对象创建场景反而增加复杂度:

// 不必要的工厂
class ButtonFactory {
  createButton(type) {
    switch(type) {
      case 'primary':
        return new PrimaryButton()
      case 'secondary':
        return new SecondaryButton()
      default:
        return new DefaultButton()
    }
  }
}

// 直接实例化更清晰
const buttonMap = {
  primary: <PrimaryButton />,
  secondary: <SecondaryButton />,
  default: <DefaultButton />
}

function Button({ type = 'default' }) {
  return buttonMap[type] || buttonMap.default
}

命令模式的过度封装

将简单操作封装为命令对象反而降低可读性:

// 过度设计的命令模式
class Command {
  execute() {}
}

class SaveCommand extends Command {
  constructor(receiver) {
    this.receiver = receiver
  }
  
  execute() {
    this.receiver.save()
  }
}

// 使用时
const command = new SaveCommand(editor)
command.execute()

// 直接调用更清晰
editor.save()

仅在需要撤销/重做等高级功能时才适用命令模式。

适配器模式的接口污染

适配器可能掩盖底层接口设计问题:

// 反模式:用适配器掩盖设计缺陷
class BadAPI {
  getData() {
    return fetch('/legacy-endpoint')
      .then(res => res.json())
      .then(data => ({
        items: data.records,
        meta: data.info
      }))
  }
}

// 更好的做法是直接修复API设计
class GoodAPI {
  async getItems() {
    const res = await fetch('/modern-endpoint')
    return res.json()
  }
}

模板方法模式的继承陷阱

传统实现方式导致子类与父类强耦合:

// 脆弱的模板方法
class DataProcessor {
  process() {
    this.validate()
    this.transform()
    this.save()
  }
  
  validate() { /* 默认实现 */ }
  transform() { /* 抽象方法 */ }
  save() { /* 默认实现 */ }
}

// 改用组合策略更灵活
function createProcessor({ validate, transform, save }) {
  return {
    process() {
      validate?.()
      transform()
      save?.()
    }
  }
}

状态模式的状态爆炸

复杂状态机可能导致状态类数量激增:

// 状态类过多
class Order {
  state = new DraftState(this)

  setState(state) {
    this.state = state
  }
}

class DraftState { /* ... */ }
class PendingState { /* ... */ }
class PaidState { /* ... */ }
class ShippedState { /* ... */ }
class CancelledState { /* ... */ }

可考虑状态表驱动:

const stateMachine = {
  draft: {
    submit: 'pending',
    cancel: 'cancelled'
  },
  pending: {
    pay: 'paid',
    cancel: 'cancelled'
  },
  paid: {
    ship: 'shipped'
  }
}

function transition(state, action) {
  return stateMachine[state]?.[action] || state
}

中介者模式的上帝对象

中介者可能演变为过度集中的控制中心:

// 承担过多职责的中介者
class ChatRoom {
  constructor() {
    this.users = []
  }
  
  register(user) {
    this.users.push(user)
    user.room = this
  }
  
  send(message, from) {
    this.users.forEach(user => {
      if (user !== from) {
        user.receive(message)
      }
    })
  }
  
  // 逐渐添加更多业务逻辑...
  kickUser() {}
  changeTopic() {}
  setRules() {}
}

应遵循单一职责原则拆分功能:

class UserManager {
  addUser() {}
  removeUser() {}
}

class MessageDispatcher {
  broadcast() {}
  privateMessage() {}
}

class RoomPolicy {
  setRules() {}
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌