您现在的位置是:网站首页 > 拒绝设计模式(“设计模式都是花架子”)文章详情

拒绝设计模式(“设计模式都是花架子”)

设计模式在前端开发中经常被奉为圭臬,但过度依赖它们可能导致代码复杂化。有些场景下,简单直接的实现反而更高效,盲目套用模式可能适得其反。

设计模式的本质与滥用

设计模式本质是解决特定场景问题的经验总结,而非银弹。前端领域常见的滥用现象包括:

  1. 过度工程化:为简单的数据展示强行引入Redux
  2. 模式嵌套:在200行代码里同时使用工厂模式+策略模式+装饰器
  3. 形式主义:为了"符合模式"而扭曲业务逻辑
// 典型滥用示例:简单表单验证硬套策略模式
const strategies = {
  isNonEmpty: (value) => value !== '',
  minLength: (value, length) => value.length >= length,
  isMobile: (value) => /^1[3-9]\d{9}$/.test(value)
}

class Validator {
  constructor() {
    this.cache = []
  }
  add(dom, rule, errMsg) {
    // ...繁琐的策略注册逻辑
  }
  start() {
    // ...复杂的验证执行流程
  }
}

// 实际用普通函数更直观
function validateForm(data) {
  if (!data.name) return '姓名不能为空'
  if (data.phone.length !== 11) return '手机号格式错误'
  // ...其他简单校验
}

前端特有的轻量级实践

现代前端框架已经内置了许多模式的最佳实践,额外引入经典设计模式可能多余:

  1. React Hooks 替代了大部分观察者模式场景
  2. Vue Composition API 比传统工厂模式更灵活
  3. Svelte响应式系统 自动处理状态变更通知
// 使用React Context比传统中介者模式更简洁
const UserContext = createContext()

function App() {
  const [user, setUser] = useState(null)
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Navbar />
      <Profile />
    </UserContext.Provider>
  )
}

function Profile() {
  const { user } = useContext(UserContext)
  return <div>{user?.name}</div>
}

模式替代方案的具体案例

状态管理场景

Redux典型实现需要定义action、reducer、store等样板代码。现代方案如Zustand大幅简化:

// 传统Redux
const store = createStore(reducer, applyMiddleware(thunk))

// Zustand实现同等功能
const useStore = create(set => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
  decrement: () => set(state => ({ count: state.count - 1 }))
}))

DOM操作场景

jQuery时代常用的命令式操作,现在完全可以用声明式替代:

// 传统命令式DOM操作
$('.btn').on('click', function() {
  $(this).toggleClass('active')
  $('#panel').slideToggle()
})

// 现代声明式方案
function Button() {
  const [active, setActive] = useState(false)
  return (
    <>
      <button 
        className={active ? 'active' : ''}
        onClick={() => setActive(!active)}
      >
        Toggle
      </button>
      <div style={{ display: active ? 'block' : 'none' }}>
        Content
      </div>
    </>
  )
}

何时真正需要设计模式

并非全盘否定设计模式,而是强调按需使用:

  1. 复杂状态机:游戏开发中可能需要状态模式
  2. 可插拔架构:需要支持动态扩展的插件系统
  3. 高频重构场景:早期采用桥接模式为未来留余地
// 适合使用装饰器模式的场景:API请求日志
function logRequest(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value
  descriptor.value = async function(...args: any[]) {
    console.log(`Calling ${key} with`, args)
    const result = await original.apply(this, args)
    console.log(`Called ${key}, result`, result)
    return result
  }
  return descriptor
}

class ApiClient {
  @logRequest
  async getUser(id: string) {
    return fetch(`/users/${id}`)
  }
}

前端开发的实用主义原则

  1. YAGNI原则 (You Aren't Gonna Need It)

    • 不要为假设的未来需求提前引入模式
    • 每次提交的代码应该解决当前具体问题
  2. KISS原则 (Keep It Simple, Stupid)

    • 表单验证直接用if-else可能比策略模式更合适
    • 组件通信优先考虑props drilling而非立即上状态管理
  3. 可读性优先

    • 新同事能否在5分钟内理解这段代码?
    • 模式使用是否让代码更清晰而非更晦涩?
// 符合实用主义的代码示例
function calculatePrice(items) {
  let total = 0
  
  // 清晰可见的折扣规则
  if (items.length > 10) {
    total = items.reduce((sum, item) => sum + item.price * 0.8, 0)
  } 
  else if (items.some(item => item.isPremium)) {
    total = items.reduce((sum, item) => sum + (item.isPremium ? item.price * 0.9 : item.price), 0)
  }
  else {
    total = items.reduce((sum, item) => sum + item.price, 0)
  }
  
  return total > 1000 ? total * 0.95 : total
}

框架语境下的模式演进

现代框架自身已经实现了许多经典模式:

  1. React Fiber架构:本质是责任链模式+调度器模式
  2. Vue响应式系统:自动依赖追踪的观察者模式变体
  3. Svelte编译优化:相当于内置的享元模式实现

开发者应该优先掌握框架本身的机制,而非强行套用传统模式:

// React新文档推荐的简单状态管理
function useCounter() {
  const [count, setCount] = useState(0)
  const increment = () => setCount(c => c + 1)
  return { count, increment }
}

function App() {
  const counter1 = useCounter()
  const counter2 = useCounter()
  
  return (
    <>
      <button onClick={counter1.increment}>
        Counter 1: {counter1.count}
      </button>
      <button onClick={counter2.increment}>
        Counter 2: {counter2.count}
      </button>
    </>
  )
}

代码坏味道的识别标准

当出现以下症状时,可能需要重新评估模式使用:

  1. 文件跳转过多:需要查看5个以上文件才能理解核心逻辑
  2. 模式嵌套过深:一个功能同时使用3种以上设计模式
  3. 修改成本剧增:简单需求变更需要修改多处模式相关代码
  4. 团队认知负担:每次代码评审都要解释模式实现细节
// 坏味道示例:过度抽象的工厂模式
class ButtonFactory {
  static createButton(type) {
    switch(type) {
      case 'primary':
        return new PrimaryButton()
      case 'danger':
        return new DangerButton()
      default:
        return new DefaultButton()
    }
  }
}

// 更直接的实现
function Button({ type, children }) {
  const className = `btn ${type === 'primary' ? 'btn-primary' : 
                    type === 'danger' ? 'btn-danger' : 'btn-default'}`
  return <button className={className}>{children}</button>
}

前端特定领域的简化模式

某些传统模式在前端领域有更轻量的替代方案:

传统模式 前端简化方案
观察者模式 CustomEvent / EventEmitter
装饰器模式 HOC / Hook包装
适配器模式 中间件函数
单例模式 模块导出实例
// 事件总线替代观察者模式
const bus = new EventTarget()

// 发布事件
bus.dispatchEvent(new CustomEvent('login', { detail: user }))

// 订阅事件
bus.addEventListener('login', (e) => {
  console.log('User logged in:', e.detail)
})

性能视角的模式取舍

某些模式会带来性能损耗,需要权衡:

  1. 虚拟代理模式:可能增加内存占用
  2. 装饰器链:函数调用栈变深影响执行速度
  3. 复杂观察者:频繁事件触发导致渲染卡顿
// 性能敏感的动画场景应避免过度抽象
function animateElement(el) {
  // 直接操作比经过多层代理更高效
  let start = null
  function step(timestamp) {
    if (!start) start = timestamp
    const progress = timestamp - start
    el.style.transform = `translateX(${Math.min(progress / 10, 200)}px)`
    if (progress < 2000) {
      window.requestAnimationFrame(step)
    }
  }
  window.requestAnimationFrame(step)
}

团队协作的编码共识

在团队环境中更应注重实际效果:

  1. 模式采用标准:建立明确的模式使用checklist
  2. 代码审查重点:是否真的解决了问题而非是否"够模式化"
  3. 文档规范:记录已被验证有效的模式应用场景
<!-- 团队文档示例 -->
## 状态管理采用标准

✅ 采用条件:
- 超过3个层级组件需要共享状态
- 存在复杂的跨组件状态更新逻辑
- 需要持久化/回放状态历史

❌ 避免场景:
- 仅父子组件通信
- 简单表单状态
- 一次性页面状态

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步