编程常用模式是指在软件设计中反复出现、经过验证的解决方案。它们不是现成的代码库,而是解决特定问题的设计模板。掌握这些模式可以让你写出更优雅、可维护、可扩展的代码。
📚 模式的三大分类
根据 GoF(Gang of Four,四人组)的经典分类,设计模式分为三大类:
| 类别 | 作用 | 典型模式 |
|---|---|---|
| 创建型模式 | 处理对象创建机制 | 单例、工厂、建造者 |
| 结构型模式 | 处理类/对象的组合 | 适配器、装饰器、代理 |
| 行为型模式 | 处理对象间的通信 | 观察者、策略、职责链 |
🏭 创建型模式 (Creational Patterns)
1. 单例模式 (Singleton)
确保一个类只有一个实例,并提供一个全局访问点。
// 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)
定义一个创建对象的接口,让子类决定实例化哪个类。
// 简单工厂
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)
将一个复杂对象的构建过程与其表示分离。
// 建造者模式
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)
将一个类的接口转换成客户端期望的另一个接口。
// 旧系统 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)
动态地给对象添加额外的职责。
// 装饰器模式
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)
为另一个对象提供一个替身或占位符以控制对这个对象的访问。
// 代理模式
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)
定义对象间的一对多依赖关系,当一个对象改变状态时,所有依赖者都会收到通知。
// 观察者模式
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)
定义一系列算法,把它们封装起来,并使它们可以互相替换。
// 策略模式
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)
为请求创建一条处理者链,每个处理者依次处理请求。
// 职责链模式
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 中的逻辑复用模式。
// 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 中的逻辑复用模式。
// 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)
分离逻辑和展示。
// 容器组件 - 负责逻辑
// 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,便于切换算法 |
| 需要控制对象访问 | 代理模式 | 延迟加载、访问控制、日志记录 |
| 处理流程有多个步骤 | 职责链模式 | 解耦发送者和接收者 |
| 构建复杂对象 | 建造者模式 | 分步骤构建,参数可选 |
| 接口不兼容 | 适配器模式 | 让不兼容的类能一起工作 |
💡 模式使用原则
- 优先组合而非继承:大多数模式都强调组合优于继承
- 面向接口编程:依赖抽象而非具体实现
- 封装变化:找到系统中变化的部分并封装起来
- 开闭原则:对扩展开放,对修改封闭
- 单一职责:每个类只有一个改变的理由
掌握这些模式不是要你在所有地方都使用它们,而是在遇到合适的问题时,能够想到并应用对应的解决方案。模式是工具,不是教条。