编程常用模式集合

编程常用模式是指在软件设计中反复出现、经过验证的解决方案 。它们不是现成的代码库,而是解决特定问题的设计模板。掌握这些模式可以让你写出更优雅、可维护、可扩展的代码。

📚 模式的三大分类

根据 GoF(Gang of Four,四人组)的经典分类,设计模式分为三大类:

类别 作用 典型模式
创建型模式 处理对象创建机制 单例、工厂、建造者
结构型模式 处理类/对象的组合 适配器、装饰器、代理
行为型模式 处理对象间的通信 观察者、策略、职责链

🏭 创建型模式 (Creational Patterns)

1. 单例模式 (Singleton)

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

typescript 复制代码
// TypeScript 实现
class ConfigManager {
  private static instance: ConfigManager
  private settings: Map<string, string> = new Map()
  
  private constructor() {} // 私有构造函数,防止外部 new
  
  public static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager()
    }
    return ConfigManager.instance
  }
  
  public set(key: string, value: string): void {
    this.settings.set(key, value)
  }
  
  public get(key: string): string | undefined {
    return this.settings.get(key)
  }
}

// 使用
const config1 = ConfigManager.getInstance()
const config2 = ConfigManager.getInstance()
config1.set('theme', 'dark')
console.log(config2.get('theme')) // 'dark' - 同一个实例

// Vue/Pinia 中的单例
// store 实际上是单例模式的应用
const useUserStore = defineStore('user', {
  state: () => ({ name: 'John' })
})

2. 工厂模式 (Factory Method)

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

typescript 复制代码
// 简单工厂
interface Button {
  render(): void
  onClick(fn: () => void): void
}

class WindowsButton implements Button {
  render() { console.log('渲染 Windows 风格按钮') }
  onClick(fn: () => void) { console.log('Windows 点击事件') }
}

class MacButton implements Button {
  render() { console.log('渲染 Mac 风格按钮') }
  onClick(fn: () => void) { console.log('Mac 点击事件') }
}

class ButtonFactory {
  static createButton(os: 'windows' | 'mac'): Button {
    switch (os) {
      case 'windows': return new WindowsButton()
      case 'mac': return new MacButton()
      default: throw new Error('不支持的操作系统')
    }
  }
}

// 使用
const button = ButtonFactory.createButton('windows')
button.render()

// Vue 中的应用:渲染函数和组件
const ButtonComponent = {
  props: ['type'],
  render() {
    switch (this.type) {
      case 'primary': return h(PrimaryButton)
      case 'danger': return h(DangerButton)
      default: return h(DefaultButton)
    }
  }
}

3. 建造者模式 (Builder)

将一个复杂对象的构建过程与其表示分离。

typescript 复制代码
// 建造者模式
class HttpRequest {
  private url: string = ''
  private method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET'
  private headers: Record<string, string> = {}
  private body?: any
  private timeout: number = 5000
  
  // 私有构造函数,只能通过 Builder 创建
  private constructor() {}
  
  static get Builder() {
    class HttpRequestBuilder {
      private request: HttpRequest
      
      constructor() {
        this.request = new HttpRequest()
      }
      
      setUrl(url: string): this {
        this.request.url = url
        return this
      }
      
      setMethod(method: HttpRequest['method']): this {
        this.request.method = method
        return this
      }
      
      addHeader(key: string, value: string): this {
        this.request.headers[key] = value
        return this
      }
      
      setBody(body: any): this {
        this.request.body = body
        return this
      }
      
      setTimeout(timeout: number): this {
        this.request.timeout = timeout
        return this
      }
      
      build(): HttpRequest {
        // 可以在这里添加验证逻辑
        if (!this.request.url) {
          throw new Error('URL 是必填项')
        }
        return this.request
      }
    }
    return HttpRequestBuilder
  }
}

// 使用
const request = new HttpRequest.Builder()
  .setUrl('/api/users')
  .setMethod('POST')
  .addHeader('Content-Type', 'application/json')
  .setBody({ name: 'John' })
  .setTimeout(10000)
  .build()

🏗️ 结构型模式 (Structural Patterns)

4. 适配器模式 (Adapter)

将一个类的接口转换成客户端期望的另一个接口。

typescript 复制代码
// 旧系统 API
class OldPaymentSystem {
  processPaymentInDollars(amount: number): void {
    console.log(`处理 ${amount} 美元支付`)
  }
}

// 新系统期望的接口
interface NewPaymentProcessor {
  pay(amountInCents: number): void
}

// 适配器
class PaymentAdapter implements NewPaymentProcessor {
  constructor(private oldSystem: OldPaymentSystem) {}
  
  pay(amountInCents: number): void {
    // 转换:分 -> 美元
    const dollars = amountInCents / 100
    this.oldSystem.processPaymentInDollars(dollars)
  }
}

// 使用
const oldSystem = new OldPaymentSystem()
const adapted = new PaymentAdapter(oldSystem)
adapted.pay(1299) // "处理 12.99 美元支付"

// Vue 中的应用:适配不同格式的数据
function adaptUserData(apiData: any): User {
  return {
    fullName: `${apiData.first_name} ${apiData.last_name}`,
    age: apiData.age,
    email: apiData.email_address
  }
}

5. 装饰器模式 (Decorator)

动态地给对象添加额外的职责。

typescript 复制代码
// 装饰器模式
interface Coffee {
  cost(): number
  description(): string
}

class SimpleCoffee implements Coffee {
  cost(): number { return 10 }
  description(): string { return '普通咖啡' }
}

// 装饰器基类
abstract class CoffeeDecorator implements Coffee {
  constructor(protected coffee: Coffee) {}
  
  abstract cost(): number
  abstract description(): string
}

// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
  cost(): number {
    return this.coffee.cost() + 2
  }
  
  description(): string {
    return `${this.coffee.description()} + 牛奶`
  }
}

class SugarDecorator extends CoffeeDecorator {
  cost(): number {
    return this.coffee.cost() + 1
  }
  
  description(): string {
    return `${this.coffee.description()} + 糖`
  }
}

// 使用
let coffee: Coffee = new SimpleCoffee()
coffee = new MilkDecorator(coffee)
coffee = new SugarDecorator(coffee)

console.log(coffee.description()) // "普通咖啡 + 牛奶 + 糖"
console.log(coffee.cost()) // 13

// TypeScript 中的装饰器(提案阶段)
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value
  descriptor.value = function(...args: any[]) {
    console.log(`调用 ${propertyKey} 参数:`, args)
    return original.apply(this, args)
  }
}

class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b
  }
}

6. 代理模式 (Proxy)

为另一个对象提供一个替身或占位符以控制对这个对象的访问。

typescript 复制代码
// 代理模式
interface Image {
  display(): void
}

class RealImage implements Image {
  constructor(private filename: string) {
    this.loadFromDisk()
  }
  
  private loadFromDisk(): void {
    console.log(`加载图片: ${this.filename}`)
  }
  
  display(): void {
    console.log(`显示图片: ${this.filename}`)
  }
}

class ImageProxy implements Image {
  private realImage: RealImage | null = null
  
  constructor(private filename: string) {}
  
  display(): void {
    if (!this.realImage) {
      this.realImage = new RealImage(this.filename)
    }
    this.realImage.display()
  }
}

// 使用
const image = new ImageProxy('photo.jpg')
// 图片不会立即加载,只有在 display 时才加载
image.display() // 加载并显示
image.display() // 只显示(已加载过)

// JavaScript 原生 Proxy
const handler = {
  get: function(target: any, prop: string) {
    console.log(`访问属性: ${prop}`)
    return prop in target ? target[prop] : '默认值'
  }
}

const user = new Proxy({ name: 'John' }, handler)
console.log(user.name) // "访问属性: name" + "John"
console.log(user.age)  // "访问属性: age" + "默认值"

🔄 行为型模式 (Behavioral Patterns)

7. 观察者模式 (Observer)

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

typescript 复制代码
// 观察者模式
interface Observer<T> {
  update(data: T): void
}

class Subject<T> {
  private observers: Observer<T>[] = []
  
  attach(observer: Observer<T>): void {
    this.observers.push(observer)
  }
  
  detach(observer: Observer<T>): void {
    const index = this.observers.indexOf(observer)
    if (index !== -1) {
      this.observers.splice(index, 1)
    }
  }
  
  notify(data: T): void {
    this.observers.forEach(observer => observer.update(data))
  }
}

// 具体观察者
class Logger implements Observer<string> {
  update(data: string): void {
    console.log(`日志记录: ${data}`)
  }
}

class EmailSender implements Observer<string> {
  update(data: string): void {
    console.log(`发送邮件: ${data}`)
  }
}

// 使用
const subject = new Subject<string>()
subject.attach(new Logger())
subject.attach(new EmailSender())
subject.notify('用户已注册')

// Vue 中的应用:响应式系统
// Vue 的 ref 和 reactive 就是观察者模式的实现
const state = ref({ count: 0 })
watchEffect(() => {
  console.log(`状态变化: ${state.value.count}`)
})

// EventBus 也是观察者模式
const eventBus = new EventEmitter()
eventBus.on('user-login', (user) => console.log(user))

8. 策略模式 (Strategy)

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

typescript 复制代码
// 策略模式
interface PaymentStrategy {
  pay(amount: number): void
}

class CreditCardPayment implements PaymentStrategy {
  constructor(private cardNumber: string) {}
  
  pay(amount: number): void {
    console.log(`使用信用卡 ${this.cardNumber.slice(-4)} 支付 ${amount} 元`)
  }
}

class WeChatPayment implements PaymentStrategy {
  pay(amount: number): void {
    console.log(`使用微信支付 ${amount} 元`)
  }
}

class AlipayPayment implements PaymentStrategy {
  pay(amount: number): void {
    console.log(`使用支付宝支付 ${amount} 元`)
  }
}

class ShoppingCart {
  private amount = 0
  private paymentStrategy: PaymentStrategy | null = null
  
  setPaymentStrategy(strategy: PaymentStrategy): void {
    this.paymentStrategy = strategy
  }
  
  addItem(price: number): void {
    this.amount += price
  }
  
  checkout(): void {
    if (!this.paymentStrategy) {
      throw new Error('请先选择支付方式')
    }
    this.paymentStrategy.pay(this.amount)
  }
}

// 使用
const cart = new ShoppingCart()
cart.addItem(100)
cart.addItem(200)

cart.setPaymentStrategy(new CreditCardPayment('1234567890123456'))
cart.checkout() // "使用信用卡 3456 支付 300 元"

cart.setPaymentStrategy(new WeChatPayment())
cart.checkout() // "使用微信支付 300 元"

// Vue 中的应用:表单验证策略
interface ValidationStrategy {
  validate(value: string): boolean
  errorMessage: string
}

const emailStrategy: ValidationStrategy = {
  validate: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
  errorMessage: '请输入有效的邮箱地址'
}

const phoneStrategy: ValidationStrategy = {
  validate: (value) => /^1[3-9]\d{9}$/.test(value),
  errorMessage: '请输入有效的手机号'
}

9. 职责链模式 (Chain of Responsibility)

为请求创建一条处理者链,每个处理者依次处理请求。

typescript 复制代码
// 职责链模式
interface Handler {
  setNext(handler: Handler): Handler
  handle(request: string): string | null
}

abstract class AbstractHandler implements Handler {
  private nextHandler: Handler | null = null
  
  setNext(handler: Handler): Handler {
    this.nextHandler = handler
    return handler
  }
  
  handle(request: string): string | null {
    if (this.nextHandler) {
      return this.nextHandler.handle(request)
    }
    return null
  }
}

class AuthHandler extends AbstractHandler {
  handle(request: string): string | null {
    if (request.includes('token')) {
      console.log('认证通过')
      return super.handle(request)
    }
    return '认证失败'
  }
}

class ValidationHandler extends AbstractHandler {
  handle(request: string): string | null {
    if (request.length > 10) {
      console.log('数据验证通过')
      return super.handle(request)
    }
    return '数据验证失败'
  }
}

class CacheHandler extends AbstractHandler {
  handle(request: string): string | null {
    console.log('检查缓存...')
    // 模拟缓存命中
    if (Math.random() > 0.5) {
      return '返回缓存数据'
    }
    return super.handle(request)
  }
}

// 使用
const auth = new AuthHandler()
const validation = new ValidationHandler()
const cache = new CacheHandler()

auth.setNext(validation).setNext(cache)

const result = auth.handle('request-with-token-and-long-enough')
console.log(result)

// Web 开发中的应用:中间件
// Express/Koa 中间件就是职责链模式
app.use((req, res, next) => {
  console.log('中间件1')
  next()
})

app.use((req, res, next) => {
  console.log('中间件2')
  next()
})

🎯 现代前端特有模式

10. 组合式函数模式 (Composables)

Vue 3 中的逻辑复用模式。

typescript 复制代码
// composables/useMouse.ts
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)
  
  function update(event: MouseEvent) {
    x.value = event.clientX
    y.value = event.clientY
  }
  
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
  
  return { x, y }
}

// 使用
const { x, y } = useMouse()

11. 渲染道具模式 (Render Props)

React 中的逻辑复用模式。

tsx 复制代码
// Render Props 模式
interface MouseTrackerProps {
  render: (state: { x: number; y: number }) => React.ReactNode
}

class MouseTracker extends React.Component<MouseTrackerProps> {
  state = { x: 0, y: 0 }
  
  handleMouseMove = (event: React.MouseEvent) => {
    this.setState({ x: event.clientX, y: event.clientY })
  }
  
  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    )
  }
}

// 使用
<MouseTracker 
  render={({ x, y }) => (
    <h1>鼠标位置: {x}, {y}</h1>
  )}
/>

12. 容器组件模式 (Container/Presentational)

分离逻辑和展示。

typescript 复制代码
// 容器组件 - 负责逻辑
// containers/UserContainer.tsx
import { useState, useEffect } from 'react'
import UserList from '../presentational/UserList'

export default function UserContainer() {
  const [users, setUsers] = useState([])
  const [loading, setLoading] = useState(true)
  
  useEffect(() => {
    fetch('/api/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data)
        setLoading(false)
      })
  }, [])
  
  return (
    <UserList 
      users={users}
      loading={loading}
    />
  )
}

// 展示组件 - 负责 UI
// presentational/UserList.tsx
interface Props {
  users: User[]
  loading: boolean
}

export default function UserList({ users, loading }: Props) {
  if (loading) return <div>加载中...</div>
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

📊 模式选择指南

场景 推荐模式 理由
需要全局唯一实例 单例模式 确保配置、连接池等只有一个实例
对象创建逻辑复杂 工厂模式 封装创建细节,便于扩展
需要动态添加功能 装饰器模式 比继承更灵活,符合开闭原则
一对多依赖关系 观察者模式 对象状态变化时自动通知依赖者
算法可以互相替换 策略模式 避免大量 if-else,便于切换算法
需要控制对象访问 代理模式 延迟加载、访问控制、日志记录
处理流程有多个步骤 职责链模式 解耦发送者和接收者
构建复杂对象 建造者模式 分步骤构建,参数可选
接口不兼容 适配器模式 让不兼容的类能一起工作

💡 模式使用原则

  1. 优先组合而非继承:大多数模式都强调组合优于继承
  2. 面向接口编程:依赖抽象而非具体实现
  3. 封装变化:找到系统中变化的部分并封装起来
  4. 开闭原则:对扩展开放,对修改封闭
  5. 单一职责:每个类只有一个改变的理由

掌握这些模式不是要你在所有地方都使用它们,而是在遇到合适的问题时,能够想到并应用对应的解决方案。模式是工具,不是教条。

相关推荐
时光不负努力2 小时前
ts+vue3开发规范
vue.js·typescript
大雨还洅下2 小时前
前端JS: 跨域解决
javascript
恋猫de小郭2 小时前
Apple 的 ANE 被挖掘,AI 硬件公开,宣传的 38 TOPS 居然是"数字游戏"?
前端·人工智能·ios
小岛前端2 小时前
Node.js 宣布重大调整,运行十年的规则要改了!
前端·node.js
OpenTiny社区2 小时前
OpenTiny NEXT-SDK 重磅发布:四步把你的前端应用变成智能应用
前端·javascript·ai编程
梦想CAD控件2 小时前
在线CAD开发包结构与功能说明
前端·javascript·vue.js
张拭心2 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
时光不负努力2 小时前
typescript常用的dom 元素类型
前端·typescript
小怪点点2 小时前
大文件切片上传
前端