DevToolBox免费
博客

软件设计模式指南:创建型、结构型与行为型模式

22 分钟阅读作者 DevToolBox Team

软件设计模式完全指南:TypeScript 与 Python 实战示例

全面掌握 23 种 GoF 设计模式 — 创建型(工厂、建造者、单例)、结构型(适配器、装饰器、代理、外观)和行为型(观察者、策略、命令、状态)— 配合生产级 TypeScript 和 Python 代码示例。

TL;DR — 60 秒速览设计模式
  • Factory:将对象创建委托给工厂方法,解耦客户端与具体类
  • Builder:分步构建复杂对象,支持链式调用和可选参数
  • Singleton:确保全局唯一实例(谨慎使用,优先考虑 DI)
  • Adapter / Facade:统一不兼容接口 / 为复杂子系统提供简单入口
  • Decorator / Proxy:动态添加行为 / 控制对象访问(缓存、日志、权限)
  • Observer:一对多通知机制,驱动事件系统和响应式编程
  • Strategy / Command / State:可互换算法 / 封装请求为对象 / 基于状态切换行为
核心要点
  • 优先组合而非继承 — 大多数模式都体现了这一原则
  • 面向接口编程,而非面向实现 — 降低耦合度
  • 开闭原则(OCP):对扩展开放,对修改关闭
  • 不要为了使用模式而使用模式 — 只在真正需要时引入
  • TypeScript 接口和 Python Protocol 是类型安全模式的基石
  • 现代框架已内置许多模式(React Context=Observer,Express 中间件=Chain of Responsibility)

设计模式概览

设计模式分为三大类。下表列出了本指南涵盖的 11 种最常用模式及其核心意图。

类别模式核心意图
创建型Factory将对象创建委托给子类或工厂方法
创建型Builder分步构建复杂对象
创建型Singleton保证类只有一个实例
结构型Adapter转换不兼容接口为可协作的接口
结构型Decorator动态为对象添加新行为
结构型Proxy为对象提供替代或占位符以控制访问
结构型Facade为复杂子系统提供简化接口
行为型Observer定义一对多依赖关系,自动通知变更
行为型Strategy定义可互换的算法族
行为型Command将请求封装为对象,支持撤销和排队
行为型State根据内部状态改变对象行为

一、创建型模式

1.1 工厂方法模式 (Factory Method)

工厂方法定义一个创建对象的接口,让子类决定实例化哪个类。它将 new 操作延迟到子类,使代码对扩展开放、对修改关闭。

TypeScript

// Factory Method — TypeScript
interface Notification {
  send(message: string): void;
}

class EmailNotification implements Notification {
  send(message: string): void {
    console.log("Email: " + message);
  }
}

class SMSNotification implements Notification {
  send(message: string): void {
    console.log("SMS: " + message);
  }
}

class PushNotification implements Notification {
  send(message: string): void {
    console.log("Push: " + message);
  }
}

type Channel = "email" | "sms" | "push";

function createNotification(channel: Channel): Notification {
  const map: Record<Channel, () => Notification> = {
    email: () => new EmailNotification(),
    sms:   () => new SMSNotification(),
    push:  () => new PushNotification(),
  };
  return map[channel]();
}

// Usage
const notifier = createNotification("email");
notifier.send("Welcome aboard!");  // Email: Welcome aboard!

Python

# Factory Method — Python
from abc import ABC, abstractmethod

class Notification(ABC):
    @abstractmethod
    def send(self, message: str) -> None: ...

class EmailNotification(Notification):
    def send(self, message: str) -> None:
        print(f"Email: {message}")

class SMSNotification(Notification):
    def send(self, message: str) -> None:
        print(f"SMS: {message}")

class PushNotification(Notification):
    def send(self, message: str) -> None:
        print(f"Push: {message}")

def create_notification(channel: str) -> Notification:
    factories = {
        "email": EmailNotification,
        "sms":   SMSNotification,
        "push":  PushNotification,
    }
    if channel not in factories:
        raise ValueError(f"Unknown channel: {channel}")
    return factories[channel]()

# Usage
notifier = create_notification("sms")
notifier.send("Your code is 1234")  # SMS: Your code is 1234

1.2 建造者模式 (Builder)

建造者模式将复杂对象的构建过程与表示分离,使同样的构建过程可以创建不同的表示。当对象具有大量可选参数或需要特定顺序构建时,Builder 比构造函数重载更优雅。

TypeScript

// Builder — TypeScript
interface QueryConfig {
  table: string;
  fields: string[];
  conditions: string[];
  orderBy?: string;
  limit?: number;
}

class QueryBuilder {
  private config: QueryConfig;

  constructor(table: string) {
    this.config = { table, fields: ["*"], conditions: [] };
  }

  select(...fields: string[]): this {
    this.config.fields = fields;
    return this;
  }

  where(condition: string): this {
    this.config.conditions.push(condition);
    return this;
  }

  orderBy(field: string): this {
    this.config.orderBy = field;
    return this;
  }

  limit(n: number): this {
    this.config.limit = n;
    return this;
  }

  build(): string {
    let sql = "SELECT " + this.config.fields.join(", ");
    sql += " FROM " + this.config.table;
    if (this.config.conditions.length > 0) {
      sql += " WHERE " + this.config.conditions.join(" AND ");
    }
    if (this.config.orderBy) {
      sql += " ORDER BY " + this.config.orderBy;
    }
    if (this.config.limit !== undefined) {
      sql += " LIMIT " + this.config.limit;
    }
    return sql;
  }
}

// Usage — fluent chaining
const query = new QueryBuilder("users")
  .select("id", "name", "email")
  .where("active = true")
  .where("age > 18")
  .orderBy("name")
  .limit(50)
  .build();

console.log(query);
// SELECT id, name, email FROM users WHERE active = true AND age > 18 ORDER BY name LIMIT 50

Python

# Builder — Python
from dataclasses import dataclass, field

@dataclass
class QueryConfig:
    table: str
    fields: list[str] = field(default_factory=lambda: ["*"])
    conditions: list[str] = field(default_factory=list)
    order_by: str | None = None
    limit: int | None = None

class QueryBuilder:
    def __init__(self, table: str):
        self._config = QueryConfig(table=table)

    def select(self, *fields: str) -> "QueryBuilder":
        self._config.fields = list(fields)
        return self

    def where(self, condition: str) -> "QueryBuilder":
        self._config.conditions.append(condition)
        return self

    def order_by(self, col: str) -> "QueryBuilder":
        self._config.order_by = col
        return self

    def limit(self, n: int) -> "QueryBuilder":
        self._config.limit = n
        return self

    def build(self) -> str:
        sql = f"SELECT {', '.join(self._config.fields)} FROM {self._config.table}"
        if self._config.conditions:
            sql += f" WHERE {' AND '.join(self._config.conditions)}"
        if self._config.order_by:
            sql += f" ORDER BY {self._config.order_by}"
        if self._config.limit is not None:
            sql += f" LIMIT {self._config.limit}"
        return sql

# Usage
query = (
    QueryBuilder("users")
    .select("id", "name", "email")
    .where("active = true")
    .order_by("name")
    .limit(50)
    .build()
)
print(query)

1.3 单例模式 (Singleton)

单例模式确保一个类只有一个实例,并提供全局访问点。在现代开发中,依赖注入通常是更好的选择,但 Singleton 在管理配置、日志和连接池时仍然有用。

TypeScript

// Singleton — TypeScript
class Logger {
  private static instance: Logger;
  private logs: string[] = [];

  private constructor() {} // prevent external instantiation

  static getInstance(): Logger {
    if (!Logger.instance) {
      Logger.instance = new Logger();
    }
    return Logger.instance;
  }

  log(message: string): void {
    const entry = "[" + new Date().toISOString() + "] " + message;
    this.logs.push(entry);
    console.log(entry);
  }

  getHistory(): string[] {
    return [...this.logs];
  }
}

// Usage — same instance everywhere
const logger1 = Logger.getInstance();
const logger2 = Logger.getInstance();
console.log(logger1 === logger2);  // true

logger1.log("App started");
logger2.log("User logged in");
console.log(logger1.getHistory().length);  // 2

Python

# Singleton — Python (using __new__)
class Logger:
    _instance = None
    _logs: list[str] = []

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def log(self, message: str) -> None:
        from datetime import datetime
        entry = f"[{datetime.now().isoformat()}] {message}"
        self._logs.append(entry)
        print(entry)

    def get_history(self) -> list[str]:
        return list(self._logs)

# Usage
logger1 = Logger()
logger2 = Logger()
print(logger1 is logger2)  # True

# Alternative: module-level singleton (Pythonic approach)
# config.py
class _Config:
    def __init__(self):
        self.debug = False
        self.db_url = "sqlite:///app.db"

config = _Config()  # module-level instance acts as singleton

二、结构型模式

2.1 适配器模式 (Adapter)

适配器模式将一个类的接口转换成客户端期望的另一个接口。它让原本接口不兼容的类可以协同工作,常用于集成第三方库或遗留系统。

TypeScript

// Adapter — TypeScript
// Target interface our app expects
interface PaymentProcessor {
  charge(amount: number, currency: string): Promise<{ id: string; status: string }>;
}

// Legacy third-party SDK with incompatible interface
class LegacyPaymentSDK {
  makePayment(cents: number, curr: string, callback: (err: Error | null, txId: string) => void): void {
    setTimeout(() => callback(null, "txn_" + Math.random().toString(36).slice(2)), 100);
  }
}

// Adapter wraps the legacy SDK to match our interface
class LegacyPaymentAdapter implements PaymentProcessor {
  constructor(private sdk: LegacyPaymentSDK) {}

  charge(amount: number, currency: string): Promise<{ id: string; status: string }> {
    return new Promise((resolve, reject) => {
      const cents = Math.round(amount * 100);
      this.sdk.makePayment(cents, currency, (err, txId) => {
        if (err) reject(err);
        else resolve({ id: txId, status: "success" });
      });
    });
  }
}

// Usage — client only knows PaymentProcessor
async function checkout(processor: PaymentProcessor) {
  const result = await processor.charge(29.99, "USD");
  console.log("Payment " + result.id + ": " + result.status);
}

const adapter = new LegacyPaymentAdapter(new LegacyPaymentSDK());
checkout(adapter);

Python

# Adapter — Python
from typing import Protocol

class PaymentProcessor(Protocol):
    def charge(self, amount: float, currency: str) -> dict: ...

class LegacyPaymentSDK:
    """Third-party SDK with incompatible interface"""
    def make_payment(self, cents: int, curr: str) -> str:
        import uuid
        return f"txn_{uuid.uuid4().hex[:8]}"

class LegacyPaymentAdapter:
    """Adapter wraps legacy SDK to match PaymentProcessor protocol"""
    def __init__(self, sdk: LegacyPaymentSDK):
        self._sdk = sdk

    def charge(self, amount: float, currency: str) -> dict:
        cents = round(amount * 100)
        tx_id = self._sdk.make_payment(cents, currency)
        return {"id": tx_id, "status": "success"}

# Usage
adapter = LegacyPaymentAdapter(LegacyPaymentSDK())
result = adapter.charge(29.99, "USD")
print(f"Payment {result['id']}: {result['status']}")

2.2 装饰器模式 (Decorator)

装饰器模式动态地为对象添加额外职责,而无需修改原始类。相比继承,它更灵活,可以任意组合行为。Python 的 @decorator 语法和 TypeScript 的 Stage 3 装饰器都深受此模式影响。

TypeScript

// Decorator — TypeScript
interface HttpClient {
  request(url: string, options?: RequestInit): Promise<Response>;
}

// Base implementation
class FetchClient implements HttpClient {
  async request(url: string, options?: RequestInit): Promise<Response> {
    return fetch(url, options);
  }
}

// Decorator: adds logging
class LoggingDecorator implements HttpClient {
  constructor(private client: HttpClient) {}

  async request(url: string, options?: RequestInit): Promise<Response> {
    console.log("[LOG] Request: " + url);
    const start = Date.now();
    const response = await this.client.request(url, options);
    console.log("[LOG] " + response.status + " in " + (Date.now() - start) + "ms");
    return response;
  }
}

// Decorator: adds retry logic
class RetryDecorator implements HttpClient {
  constructor(private client: HttpClient, private maxRetries = 3) {}

  async request(url: string, options?: RequestInit): Promise<Response> {
    let lastError: Error | null = null;
    for (let i = 0; i <= this.maxRetries; i++) {
      try {
        return await this.client.request(url, options);
      } catch (err) {
        lastError = err as Error;
        console.log("[RETRY] Attempt " + (i + 1) + " failed");
      }
    }
    throw lastError;
  }
}

// Usage: compose decorators
const client: HttpClient = new LoggingDecorator(
  new RetryDecorator(
    new FetchClient(), 3
  )
);
// client.request("https://api.example.com/data");

Python

# Decorator — Python (both class-based and @decorator syntax)
import functools
import time
from typing import Callable, Any

# Function decorator for timing
def timer(func: Callable) -> Callable:
    @functools.wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

# Function decorator for retry
def retry(max_attempts: int = 3):
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Attempt {attempt} failed: {e}")
                    if attempt == max_attempts:
                        raise
        return wrapper
    return decorator

# Usage — stack decorators
@timer
@retry(max_attempts=3)
def fetch_data(url: str) -> str:
    import urllib.request
    return urllib.request.urlopen(url).read().decode()

# fetch_data("https://api.example.com/data")

2.3 代理模式 (Proxy)

代理模式为另一个对象提供替代品或占位符以控制对它的访问。常见用途包括延迟初始化(虚拟代理)、访问控制(保护代理)和缓存(智能代理)。

TypeScript

// Proxy — TypeScript (Caching Proxy)
interface DataService {
  fetchData(key: string): Promise<string>;
}

class ApiService implements DataService {
  async fetchData(key: string): Promise<string> {
    console.log("[API] Fetching " + key + " from server...");
    // Simulate network request
    return "data_for_" + key;
  }
}

class CachingProxy implements DataService {
  private cache = new Map<string, { value: string; expiry: number }>();

  constructor(private service: DataService, private ttlMs = 60000) {}

  async fetchData(key: string): Promise<string> {
    const cached = this.cache.get(key);
    if (cached && cached.expiry > Date.now()) {
      console.log("[CACHE] Hit for " + key);
      return cached.value;
    }
    console.log("[CACHE] Miss for " + key);
    const value = await this.service.fetchData(key);
    this.cache.set(key, { value, expiry: Date.now() + this.ttlMs });
    return value;
  }
}

// Usage
const service: DataService = new CachingProxy(new ApiService(), 5000);
// First call: cache miss, hits API
// Second call: cache hit, returns instantly

Python

# Proxy — Python (Caching Proxy)
import time
from typing import Protocol

class DataService(Protocol):
    def fetch_data(self, key: str) -> str: ...

class ApiService:
    def fetch_data(self, key: str) -> str:
        print(f"[API] Fetching {key} from server...")
        return f"data_for_{key}"

class CachingProxy:
    def __init__(self, service: DataService, ttl: float = 60.0):
        self._service = service
        self._ttl = ttl
        self._cache: dict[str, tuple[str, float]] = {}

    def fetch_data(self, key: str) -> str:
        if key in self._cache:
            value, expiry = self._cache[key]
            if time.time() < expiry:
                print(f"[CACHE] Hit for {key}")
                return value
        print(f"[CACHE] Miss for {key}")
        value = self._service.fetch_data(key)
        self._cache[key] = (value, time.time() + self._ttl)
        return value

# Usage
service = CachingProxy(ApiService(), ttl=5.0)
print(service.fetch_data("users"))   # [API] Fetching...
print(service.fetch_data("users"))   # [CACHE] Hit

2.4 外观模式 (Facade)

外观模式为复杂的子系统提供一个简化的统一接口。它不添加新功能,而是将多个复杂操作封装在一个高级 API 后面,降低客户端的使用复杂度。

TypeScript

// Facade — TypeScript
// Complex subsystems
class UserService {
  createUser(email: string) { return { id: 1, email }; }
}

class EmailService {
  sendWelcome(email: string) { console.log("Welcome email sent to " + email); }
}

class AnalyticsService {
  trackSignup(userId: number) { console.log("Signup tracked for user " + userId); }
}

class BillingService {
  createTrialSubscription(userId: number) {
    console.log("14-day trial started for user " + userId);
  }
}

// Facade — single method coordinates all subsystems
class RegistrationFacade {
  private userService = new UserService();
  private emailService = new EmailService();
  private analytics = new AnalyticsService();
  private billing = new BillingService();

  async registerUser(email: string) {
    const user = this.userService.createUser(email);
    this.emailService.sendWelcome(email);
    this.analytics.trackSignup(user.id);
    this.billing.createTrialSubscription(user.id);
    return user;
  }
}

// Usage — client only interacts with the facade
const registration = new RegistrationFacade();
registration.registerUser("alice@example.com");

三、行为型模式

3.1 观察者模式 (Observer)

观察者模式定义了对象间的一对多依赖关系:当一个对象(Subject)状态变化时,所有依赖它的对象(Observer)都会收到通知并自动更新。事件总线、发布/订阅系统和 React 状态管理都是观察者模式的体现。

TypeScript

// Observer — TypeScript (Type-safe Event Emitter)
type EventMap = {
  "user:login":    { userId: string; timestamp: number };
  "user:logout":   { userId: string };
  "cart:update":   { items: number; total: number };
};

class EventBus<T extends Record<string, unknown>> {
  private listeners = new Map<keyof T, Set<(data: any) => void>>();

  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): () => void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    this.listeners.get(event)!.add(callback);
    // Return unsubscribe function
    return () => { this.listeners.get(event)?.delete(callback); };
  }

  emit<K extends keyof T>(event: K, data: T[K]): void {
    this.listeners.get(event)?.forEach((cb) => cb(data));
  }
}

// Usage
const bus = new EventBus<EventMap>();

const unsub = bus.on("user:login", (data) => {
  console.log("User " + data.userId + " logged in at " + data.timestamp);
});

bus.on("cart:update", (data) => {
  console.log("Cart: " + data.items + " items, $" + data.total);
});

bus.emit("user:login", { userId: "u_123", timestamp: Date.now() });
bus.emit("cart:update", { items: 3, total: 59.97 });

unsub(); // unsubscribe from user:login

Python

# Observer — Python
from typing import Callable, Any
from collections import defaultdict

class EventBus:
    def __init__(self):
        self._listeners: dict[str, list[Callable]] = defaultdict(list)

    def on(self, event: str, callback: Callable) -> Callable:
        self._listeners[event].append(callback)
        def unsubscribe():
            self._listeners[event].remove(callback)
        return unsubscribe

    def emit(self, event: str, **data: Any) -> None:
        for callback in self._listeners.get(event, []):
            callback(**data)

# Usage
bus = EventBus()

def on_login(user_id: str, timestamp: float):
    print(f"User {user_id} logged in at {timestamp}")

unsub = bus.on("user:login", on_login)
bus.emit("user:login", user_id="u_123", timestamp=1709000000)
unsub()  # unsubscribe

3.2 策略模式 (Strategy)

策略模式定义一族算法,将每个算法封装起来,并使它们可以互换。客户端可以在运行时选择不同的策略,而不需要修改使用策略的代码。

TypeScript

// Strategy — TypeScript
interface CompressionStrategy {
  compress(data: string): string;
  name: string;
}

class GzipStrategy implements CompressionStrategy {
  name = "gzip";
  compress(data: string): string {
    // Simulate gzip compression
    return "[gzip:" + data.length + "]" + data.slice(0, 10) + "...";
  }
}

class BrotliStrategy implements CompressionStrategy {
  name = "brotli";
  compress(data: string): string {
    // Simulate brotli compression (better ratio)
    return "[br:" + Math.floor(data.length * 0.8) + "]" + data.slice(0, 8) + "...";
  }
}

class NoCompression implements CompressionStrategy {
  name = "none";
  compress(data: string): string {
    return data;
  }
}

class FileCompressor {
  constructor(private strategy: CompressionStrategy) {}

  setStrategy(strategy: CompressionStrategy): void {
    this.strategy = strategy;
  }

  compressFile(data: string): string {
    console.log("Compressing with " + this.strategy.name);
    return this.strategy.compress(data);
  }
}

// Usage — switch strategies at runtime
const compressor = new FileCompressor(new GzipStrategy());
console.log(compressor.compressFile("Hello world repeated many times"));

compressor.setStrategy(new BrotliStrategy());
console.log(compressor.compressFile("Hello world repeated many times"));

Python

# Strategy — Python (using Protocols and first-class functions)
from typing import Protocol, Callable

# Class-based approach
class CompressionStrategy(Protocol):
    name: str
    def compress(self, data: str) -> str: ...

class GzipStrategy:
    name = "gzip"
    def compress(self, data: str) -> str:
        return f"[gzip:{len(data)}]{data[:10]}..."

class BrotliStrategy:
    name = "brotli"
    def compress(self, data: str) -> str:
        return f"[br:{int(len(data)*0.8)}]{data[:8]}..."

class FileCompressor:
    def __init__(self, strategy: CompressionStrategy):
        self._strategy = strategy

    def set_strategy(self, strategy: CompressionStrategy) -> None:
        self._strategy = strategy

    def compress_file(self, data: str) -> str:
        print(f"Compressing with {self._strategy.name}")
        return self._strategy.compress(data)

# Functional alternative — even simpler
CompressFn = Callable[[str], str]

def gzip_compress(data: str) -> str:
    return f"[gzip:{len(data)}]{data[:10]}..."

def compress_file(data: str, strategy: CompressFn = gzip_compress) -> str:
    return strategy(data)

print(compress_file("test data", gzip_compress))

3.3 命令模式 (Command)

命令模式将请求封装为独立的对象,包含执行请求所需的全部信息。它支持参数化操作、排队执行、日志记录以及可撤销的操作,是实现 undo/redo 系统的基础。

TypeScript

// Command — TypeScript (Text Editor with Undo/Redo)
interface Command {
  execute(): void;
  undo(): void;
  description: string;
}

class TextEditor {
  content = "";
  cursorPos = 0;
}

class InsertTextCommand implements Command {
  description: string;
  private previousContent = "";

  constructor(private editor: TextEditor, private text: string, private pos: number) {
    this.description = "Insert \"" + text + "\" at pos " + pos;
  }

  execute(): void {
    this.previousContent = this.editor.content;
    const before = this.editor.content.slice(0, this.pos);
    const after = this.editor.content.slice(this.pos);
    this.editor.content = before + this.text + after;
    this.editor.cursorPos = this.pos + this.text.length;
  }

  undo(): void {
    this.editor.content = this.previousContent;
    this.editor.cursorPos = this.pos;
  }
}

class DeleteTextCommand implements Command {
  description: string;
  private deletedText = "";

  constructor(private editor: TextEditor, private start: number, private end: number) {
    this.description = "Delete chars " + start + "-" + end;
  }

  execute(): void {
    this.deletedText = this.editor.content.slice(this.start, this.end);
    this.editor.content = this.editor.content.slice(0, this.start) + this.editor.content.slice(this.end);
    this.editor.cursorPos = this.start;
  }

  undo(): void {
    const before = this.editor.content.slice(0, this.start);
    const after = this.editor.content.slice(this.start);
    this.editor.content = before + this.deletedText + after;
    this.editor.cursorPos = this.start + this.deletedText.length;
  }
}

class CommandHistory {
  private undoStack: Command[] = [];
  private redoStack: Command[] = [];

  execute(command: Command): void {
    command.execute();
    this.undoStack.push(command);
    this.redoStack = []; // clear redo stack on new action
  }

  undo(): void {
    const command = this.undoStack.pop();
    if (command) {
      command.undo();
      this.redoStack.push(command);
    }
  }

  redo(): void {
    const command = this.redoStack.pop();
    if (command) {
      command.execute();
      this.undoStack.push(command);
    }
  }
}

// Usage
const editor = new TextEditor();
const history = new CommandHistory();

history.execute(new InsertTextCommand(editor, "Hello ", 0));
history.execute(new InsertTextCommand(editor, "World", 6));
console.log(editor.content); // "Hello World"

history.undo();
console.log(editor.content); // "Hello "

history.redo();
console.log(editor.content); // "Hello World"

Python

# Command — Python
from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self) -> None: ...
    @abstractmethod
    def undo(self) -> None: ...

class TextEditor:
    def __init__(self):
        self.content = ""

class InsertTextCommand(Command):
    def __init__(self, editor: TextEditor, text: str, pos: int):
        self._editor = editor
        self._text = text
        self._pos = pos
        self._prev = ""

    def execute(self) -> None:
        self._prev = self._editor.content
        before = self._editor.content[:self._pos]
        after = self._editor.content[self._pos:]
        self._editor.content = before + self._text + after

    def undo(self) -> None:
        self._editor.content = self._prev

class CommandHistory:
    def __init__(self):
        self._undo_stack: list[Command] = []
        self._redo_stack: list[Command] = []

    def execute(self, cmd: Command) -> None:
        cmd.execute()
        self._undo_stack.append(cmd)
        self._redo_stack.clear()

    def undo(self) -> None:
        if self._undo_stack:
            cmd = self._undo_stack.pop()
            cmd.undo()
            self._redo_stack.append(cmd)

# Usage
editor = TextEditor()
history = CommandHistory()
history.execute(InsertTextCommand(editor, "Hello ", 0))
history.execute(InsertTextCommand(editor, "World", 6))
print(editor.content)  # Hello World
history.undo()
print(editor.content)  # Hello 

3.4 状态模式 (State)

状态模式允许对象在内部状态改变时改变其行为,看起来好像修改了它的类。它将状态相关的行为封装在独立的状态对象中,消除了大量的条件判断语句。

TypeScript

// State — TypeScript (Order Processing)
interface OrderState {
  name: string;
  next(order: Order): void;
  cancel(order: Order): void;
}

class Order {
  state: OrderState;
  id: string;

  constructor(id: string) {
    this.id = id;
    this.state = new PendingState();
    console.log("Order " + id + " created [" + this.state.name + "]");
  }

  next(): void { this.state.next(this); }
  cancel(): void { this.state.cancel(this); }

  transitionTo(state: OrderState): void {
    console.log("Order " + this.id + ": " + this.state.name + " -> " + state.name);
    this.state = state;
  }
}

class PendingState implements OrderState {
  name = "Pending";
  next(order: Order): void {
    order.transitionTo(new PaidState());
  }
  cancel(order: Order): void {
    order.transitionTo(new CancelledState());
  }
}

class PaidState implements OrderState {
  name = "Paid";
  next(order: Order): void {
    order.transitionTo(new ShippedState());
  }
  cancel(order: Order): void {
    console.log("Order " + order.id + ": refund initiated");
    order.transitionTo(new CancelledState());
  }
}

class ShippedState implements OrderState {
  name = "Shipped";
  next(order: Order): void {
    order.transitionTo(new DeliveredState());
  }
  cancel(order: Order): void {
    console.log("Order " + order.id + ": cannot cancel — already shipped");
  }
}

class DeliveredState implements OrderState {
  name = "Delivered";
  next(order: Order): void {
    console.log("Order " + order.id + ": already delivered — no further transitions");
  }
  cancel(order: Order): void {
    console.log("Order " + order.id + ": cannot cancel — already delivered");
  }
}

class CancelledState implements OrderState {
  name = "Cancelled";
  next(order: Order): void {
    console.log("Order " + order.id + ": cancelled — no transitions allowed");
  }
  cancel(order: Order): void {
    console.log("Order " + order.id + ": already cancelled");
  }
}

// Usage
const order = new Order("ORD-001");
order.next();   // Pending -> Paid
order.next();   // Paid -> Shipped
order.cancel(); // cannot cancel — already shipped
order.next();   // Shipped -> Delivered

Python

# State — Python (Order Processing)
from abc import ABC, abstractmethod

class OrderState(ABC):
    name: str
    @abstractmethod
    def next(self, order: "Order") -> None: ...
    @abstractmethod
    def cancel(self, order: "Order") -> None: ...

class Order:
    def __init__(self, order_id: str):
        self.id = order_id
        self.state: OrderState = PendingState()

    def next(self) -> None:
        self.state.next(self)

    def cancel(self) -> None:
        self.state.cancel(self)

    def transition_to(self, state: OrderState) -> None:
        print(f"Order {self.id}: {self.state.name} -> {state.name}")
        self.state = state

class PendingState(OrderState):
    name = "Pending"
    def next(self, order: Order) -> None:
        order.transition_to(PaidState())
    def cancel(self, order: Order) -> None:
        order.transition_to(CancelledState())

class PaidState(OrderState):
    name = "Paid"
    def next(self, order: Order) -> None:
        order.transition_to(ShippedState())
    def cancel(self, order: Order) -> None:
        print(f"Order {order.id}: refund initiated")
        order.transition_to(CancelledState())

class ShippedState(OrderState):
    name = "Shipped"
    def next(self, order: Order) -> None:
        order.transition_to(DeliveredState())
    def cancel(self, order: Order) -> None:
        print(f"Order {order.id}: cannot cancel — already shipped")

class DeliveredState(OrderState):
    name = "Delivered"
    def next(self, order: Order) -> None:
        print(f"Order {order.id}: already delivered")
    def cancel(self, order: Order) -> None:
        print(f"Order {order.id}: cannot cancel — delivered")

class CancelledState(OrderState):
    name = "Cancelled"
    def next(self, order: Order) -> None:
        print(f"Order {order.id}: cancelled — no transitions")
    def cancel(self, order: Order) -> None:
        print(f"Order {order.id}: already cancelled")

# Usage
order = Order("ORD-001")
order.next()    # Pending -> Paid
order.next()    # Paid -> Shipped
order.next()    # Shipped -> Delivered

四、模式选择指南

选择正确的设计模式与正确实现一样重要。下表根据常见场景帮助你快速决策。

场景推荐模式原因
根据配置创建不同对象Factory解耦创建逻辑,易于扩展
构建具有多个可选参数的对象Builder可读性强,避免构造函数膨胀
集成遗留系统或第三方库Adapter转换接口而不修改原有代码
动态组合行为(日志+重试+缓存)Decorator运行时叠加功能,比继承灵活
控制对昂贵资源的访问Proxy懒加载、缓存、权限检查
简化复杂 API 或子系统Facade提供简洁入口,降低耦合
事件驱动通知机制Observer解耦发布者和订阅者
运行时切换算法Strategy消除条件分支,易于添加新算法
撤销/重做 或 任务队列Command请求对象化,支持日志和回滚
对象行为随状态变化State消除状态判断的 if/switch 链

五、设计模式与 SOLID 原则

设计模式并非凭空产生,它们与 SOLID 原则紧密关联。理解这种关系有助于你在合适的时机选择合适的模式。

SOLID 原则相关模式关联说明
SRPCommand, Strategy, State将职责拆分到独立的类中
OCPDecorator, Strategy, Observer扩展行为而不修改已有代码
LSPFactory, Adapter子类/适配器可替换父类/目标接口
ISPAdapter, Facade提供精简接口,不强迫实现不需要的方法
DIPFactory, Strategy, Observer依赖抽象,而非具体实现

六、常见反模式与陷阱

正确使用设计模式需要判断力。以下是开发者常犯的错误。

  • 过度设计:不要在只有一种实现时使用 Factory,或在没有撤销需求时使用 Command
  • Singleton 滥用:全局状态使测试困难,优先使用依赖注入
  • 模式叠加:不要在同一个问题上应用三四种模式,保持简单
  • 忽略语言特性:Python 的一等函数可以替代很多 Strategy/Command 类
  • 过早抽象:在看到重复之前不要引入模式,遵循三次法则(Rule of Three)

总结

设计模式是经过时间检验的解决方案,但它们不是银弹。关键在于理解每种模式解决的问题、适用的场景以及带来的权衡。在 TypeScript 中,接口和类型系统为模式提供了编译时保障;在 Python 中,鸭子类型和 Protocol 让模式实现更简洁。无论使用哪种语言,记住:代码是写给人读的,模式的最大价值在于沟通和可维护性。

常见问题

What are design patterns and why should I learn them?

Design patterns are reusable solutions to common software design problems. They provide a shared vocabulary for developers, improve code maintainability, and help you avoid reinventing the wheel. Learning them accelerates your ability to architect flexible, scalable systems and makes you a more effective collaborator.

What is the difference between Creational, Structural, and Behavioral patterns?

Creational patterns (Factory, Builder, Singleton) deal with object creation mechanisms. Structural patterns (Adapter, Decorator, Proxy, Facade) concern class and object composition. Behavioral patterns (Observer, Strategy, Command, State) focus on communication and responsibility between objects.

When should I use the Factory pattern vs the Builder pattern?

Use Factory when you need to create objects without specifying the exact class, typically when you have a family of related objects. Use Builder when constructing complex objects step by step, especially when the object has many optional parameters or requires a specific construction order.

Is Singleton an anti-pattern?

Singleton is sometimes considered an anti-pattern because it introduces global state, makes unit testing harder, and violates the Single Responsibility Principle. However, it is appropriate for genuinely shared resources like configuration managers, connection pools, or logging systems. In modern code, dependency injection often replaces Singleton.

How does the Decorator pattern differ from inheritance?

Decorator adds behavior to individual objects at runtime without affecting other instances, while inheritance extends entire classes at compile time. Decorator follows the Open/Closed Principle by allowing new behavior without modifying existing code. It also supports combining multiple behaviors dynamically, which is not possible with single inheritance.

What is the Strategy pattern and when should I use it?

The Strategy pattern defines a family of interchangeable algorithms, encapsulates each one, and makes them interchangeable at runtime. Use it when you have multiple ways to perform an operation (sorting, validation, pricing) and want to switch between them without conditional logic or modifying client code.

How do design patterns apply to TypeScript and Python differently?

TypeScript leverages interfaces, generics, and type guards to enforce pattern contracts at compile time. Python uses duck typing, protocols (PEP 544), ABC classes, and decorators. Python first-class functions often simplify patterns like Strategy and Command. TypeScript access modifiers (private, protected) enable stricter encapsulation.

Which design patterns are most commonly used in modern web development?

Observer (event systems, pub/sub, React state), Strategy (middleware, validation), Factory (API clients, component creation), Decorator (Python decorators, TypeScript decorators, higher-order components), Proxy (API gateways, caching layers), and Command (undo/redo, task queues) are the most prevalent in modern web and backend development.

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON Formatter.*Regex TesterB→Base64 Encoder

相关文章

高级TypeScript指南:泛型、条件类型、映射类型、装饰器和类型收窄

掌握高级TypeScript模式。涵盖泛型约束、带infer的条件类型、映射类型(Partial/Pick/Omit)、模板字面量类型、判别联合、工具类型深入、装饰器、模块增强、类型收窄、协变/逆变以及satisfies运算符。

系统设计指南:可扩展性、负载均衡、缓存、CAP定理和面试准备

掌握系统设计面试和实际应用。涵盖水平/垂直扩展、负载均衡、缓存(CDN、Redis)、数据库分片、CAP定理、消息队列、速率限制、URL短链接设计、社交媒体信息流以及估算计算。

React测试指南:React Testing Library、Jest、Vitest、MSW、Playwright和代码覆盖率

掌握React测试从单元测试到端到端测试。涵盖RTL查询、userEvent、renderHook、jest.mock()、Mock Service Worker、Vitest、异步测试、快照测试、Redux/Zustand测试以及Playwright vs Cypress对比。