December 20, 2025 (4mo ago)

面向对象设计模式实用指南

通过本指南掌握面向对象设计模式。学习创建型、结构型和行为型模式,并配有清晰且实用的代码示例,提升你的技能。

← Back to blog
Cover Image for 面向对象设计模式实用指南

通过本指南掌握面向对象设计模式。学习创建型、结构型和行为型模式,并配有清晰且实用的代码示例,提升你的技能。

面向对象设计模式实用指南

通过本指南掌握面向对象设计模式。学习创建型、结构型和行为型模式,并配有清晰、实用的代码示例,提升你的技能。

介绍

面向对象编程中的设计模式是为解决常见设计问题而验证过、可重用的蓝图。它们并不是可以直接复制粘贴的代码;而是可适配的模板,帮助你组织类和对象,使代码更易于维护、扩展和测试。

对常见模式——创建型、结构型和行为型——的扎实掌握,可以让你与团队成员清晰地沟通设计意图、安全地重构遗留代码,并为每个架构问题选择合适的工具。

什么是面向对象编程中的设计模式

有没有尝试过在没有计划的情况下构建复杂的东西?没有设计模式的编码可能会导致一个纠结缠绕、难以扩展和维护的代码库。设计模式就像大厨的食谱书:经过验证的配方,适配使用以产生一致且易维护的结果。

一顶厨师帽将金色字母形状滴入打开的食谱书的草图。

开发者的共同语言

模式最有价值的好处之一是它提供了共同的词汇。说出“Factory”或“Singleton”,有经验的开发者立刻能理解意图和高层结构,这加快了协作和架构决策。

“Design Patterns: Elements of Reusable Object-Oriented Software”,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 于 1994 年出版,编录了 23 个基础模式,这些仍然是 OOP 从业者的必读内容2。这些模式建立在早期的学术工作之上,展示了在大型项目中可测量的生产力和重用益处1

设计模式不是可以直接转换为代码的完成设计。它是一个描述或模板,说明如何在许多不同情况下解决一个问题。

熟练掌握多态和继承等核心 OOP 原则,有助于你更有效地应用模式。若需实用复习,请参见我们比较 polymorphism vs inheritance 的指南。

设计模式的三大核心类别

Gang of Four(四人帮)将模式组织为三类:创建型、结构型和行为型。了解这些类别可以帮助你快速为问题选择正确的方法。

图示展示创建型、结构型和行为型设计模式,使用简单的方框示意。

OOP 设计模式类别概述

模式类别核心目的常见示例
创建型管理并抽象对象创建Factory、Builder、Singleton、Prototype
结构型将类和对象组合成更大且灵活的结构Adapter、Decorator、Facade、Composite
行为型定义对象如何交互与通信Observer、Strategy、Command、Iterator

创建型模式:构建专家

创建型模式控制对象的创建方式,以避免客户端代码与具体类紧密耦合。它们隐藏创建逻辑,增加灵活性,并让你管理实例化(例如用 Singleton 强制单一实例)。

结构型模式:架构粘合剂

结构型模式帮助你将对象组装成更大的系统。它们简化组件之间的关系,使你可以更改某些部分而不破坏整个系统。像 Adapter 这样的模式允许不兼容的接口协作,这在集成第三方库时非常宝贵。

结构型模式通过识别实体之间实现关系的简单方式来简化系统设计。

行为型模式:通信指挥官

行为型模式管理对象的交互和责任分配。它们创建了干净的通信通道——Observer 用于事件驱动通知,Strategy 用于在不修改客户端的情况下替换算法。

通过谨慎管理通信路径,你可以减少依赖,使代码库更容易理解和维护。

使用创建型模式创建对象

创建型模式在对象创建周围引入一层抽象,这样你就可以在不更改客户端代码的情况下替换实现。下面是两个实用的 TypeScript 示例:Singleton 和 Factory Method。

一个插图对比单个蛋状物体与工厂装配线生产多彩物品。

Singleton 模式:确保唯一实例

Singleton 确保一个类只有一个实例并提供全局访问点。它适用于共享资源,如数据库连接或日志记录器,但它可能引入全局状态,增加测试和依赖管理的复杂性3

示例:一个用于数据库连接的 TypeScript Singleton。

class DatabaseConnection {
  private static instance: DatabaseConnection;

  private constructor() {
    // A private constructor prevents external 'new' calls
    console.log("Connecting to the database...");
  }

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

  public query(sql: string): void {
    console.log(`Executing query: ${sql}`);
  }
}

// Usage
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();

db1.query("SELECT * FROM users");
console.log(db1 === db2); // true

Singleton 的权衡:对真正的全局资源可以使用 Singleton,但更推荐使用依赖注入以获得更好的可测试性和模块化3

Factory Method:让子类决定创建什么

Factory Method 定义了创建对象的接口,而由子类决定实例化哪个具体产品。它将客户端代码与具体类解耦,使系统更容易扩展。

示例:在 TypeScript 中渲染操作系统特定的按钮。

interface Button {
  render(): void;
  onClick(f: () => void): void;
}

class WindowsButton implements Button {
  render() { console.log("Rendering a button in Windows style."); }
  onClick(f: () => void) { console.log("Windows button click event."); f(); }
}

class MacButton implements Button {
  render() { console.log("Rendering a button in macOS style."); }
  onClick(f: () => void) { console.log("Mac button click event."); f(); }
}

abstract class Dialog {
  abstract createButton(): Button;

  render() {
    const okButton = this.createButton();
    okButton.render();
  }
}

class WindowsDialog extends Dialog {
  createButton(): Button { return new WindowsButton(); }
}

class MacDialog extends Dialog {
  createButton(): Button { return new MacButton(); }
}

// Client
const os: string = "windows";
let dialog: Dialog;
if (os === "windows") dialog = new WindowsDialog(); else dialog = new MacDialog();
dialog.render();

Factory Method 使创建者不需要知道具体产品,从而使添加像 LinuxDialog 这样的新类型变得简单直接。

使用结构型模式构建灵活系统

结构型模式帮助你将对象组合成健壮且可适配的系统。两个实用的选择是 Adapter 和 Decorator。

手绘的电源适配器和代表 OOP 设计模式的一堆环的插图。

Adapter 模式:桥接不兼容接口

Adapter 将不兼容的接口包装为符合你系统期望的接口。这样可以避免对遗留代码进行侵入式更改。

示例:将 ModernLogger 适配到现有的 ILogger 接口。

class ModernLogger {
  public logInfo(message: string): void {
    console.log(`[INFO]: ${message}`);
  }
}

interface ILogger { log(message: string): void; }

class LoggerAdapter implements ILogger {
  private modernLogger: ModernLogger;
  constructor() { this.modernLogger = new ModernLogger(); }
  public log(message: string): void { this.modernLogger.logInfo(message); }
}

const logger: ILogger = new LoggerAdapter();
logger.log("User logged in successfully.");

Decorator 模式:动态添加功能

Decorator 通过包装对象在运行时添加职责。它比子类化更灵活,并遵循单一职责原则。

示例:组合一个带有可选附加项的订阅。

interface Subscription { getDescription(): string; getCost(): number; }

class BasicSubscription implements Subscription {
  getDescription(): string { return "Basic Plan"; }
  getCost(): number { return 10; }
}

abstract class SubscriptionDecorator implements Subscription {
  protected subscription: Subscription;
  constructor(subscription: Subscription) { this.subscription = subscription; }
  abstract getDescription(): string;
  abstract getCost(): number;
}

class PremiumSupportDecorator extends SubscriptionDecorator {
  getDescription(): string { return `${this.subscription.getDescription()}, Premium Support`; }
  getCost(): number { return this.subscription.getCost() + 5; }
}

class CloudStorageDecorator extends SubscriptionDecorator {
  getDescription(): string { return `${this.subscription.getDescription()}, 1TB Cloud Storage`; }
  getCost(): number { return this.subscription.getCost() + 7; }
}

let mySubscription: Subscription = new BasicSubscription();
mySubscription = new PremiumSupportDecorator(mySubscription);
mySubscription = new CloudStorageDecorator(mySubscription);
console.log(mySubscription.getDescription());
console.log(mySubscription.getCost());

这种组合式方法使特性模块化且易于测试。

使用行为型模式管理交互

行为型模式组织对象通信,使系统保持灵活和可维护。两个核心示例是 Observer 和 Strategy。

Observer 模式:通知感兴趣的方

Observer 建立一对多关系,以便当 Subject 状态改变时,Observers 会自动收到通知。它非常适合事件驱动的系统。

示例:一个简单的通知服务。

interface Subject { attach(observer: Observer): void; detach(observer: Observer): void; notify(): void; }
interface Observer { update(subject: Subject): void; }

class NotificationService implements Subject {
  public state: string = '';
  private observers: Observer[] = [];
  attach(observer: Observer): void { this.observers.push(observer); }
  detach(observer: Observer): void { const i = this.observers.indexOf(observer); if (i !== -1) this.observers.splice(i, 1); }
  notify(): void { for (const o of this.observers) o.update(this); }
  public createNewPost(title: string): void { this.state = `New Post: ${title}`; console.log(`\nNotificationService: A new post was created.`); this.notify(); }
}

class EmailNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`EmailNotifier: Sending email about "${subject.state}"`); } }
class PushNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`PushNotifier: Sending push notification for "${subject.state}"`); } }

const notificationService = new NotificationService();
const emailer = new EmailNotifier();
const pusher = new PushNotifier();
notificationService.attach(emailer);
notificationService.attach(pusher);
notificationService.createNewPost("Understanding Observer Pattern");
notificationService.detach(pusher);
notificationService.createNewPost("Why Strategy is Awesome");

Observer 产生了松耦合的设计:Subjects 不需要知道它们所通知的具体 Observers。

Strategy 模式:封装算法

Strategy 允许你在运行时替换算法,避免大量条件分支。它符合开闭原则,通过允许新增策略而不修改现有代码来实现扩展性4

示例:购物车的支付策略。

interface PaymentStrategy { pay(amount: number): void; }
class CreditCardStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} with Credit Card.`); } }
class PayPalStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} via PayPal.`); } }

class ShoppingCart {
  private paymentStrategy: PaymentStrategy;
  constructor(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
  public setPaymentStrategy(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
  public checkout(amount: number): void { this.paymentStrategy.pay(amount); }
}

const cart = new ShoppingCart(new CreditCardStrategy());
cart.checkout(150);
cart.setPaymentStrategy(new PayPalStrategy());
cart.checkout(150);

Strategy 消除了条件复杂性,使系统更易于扩展。

常见的设计陷阱与重构策略

选择一个模式只是战斗的一半。避免像 God Object(上帝对象)这样的反模式,它囤积职责并违反单一职责原则。注意代码异味——冗长的方法、过多依赖或怪物类,并通过有纪律的重构来解决它们。

重构旨在在不改变外部行为的前提下改善内部结构。这是你安全驯服遗留系统的方法。

重构为 Strategy 模式

大型的 switch 或冗长的 if/else 链是经典的代码异味。通过将可变行为提取到 Strategy 接口和具体策略类中进行重构。用对所选策略方法的单次调用替换条件分支。此更改改善了可扩展性、可测试性,并更符合 SOLID 原则4

回答关于 OOP 设计模式的常见问题

我应该学习多少种设计模式?

先专注于 5–7 个核心模式:Singleton、Factory、Adapter、Decorator、Observer 和 Strategy。了解每个模式解决的问题;一旦你掌握了这些,学习额外的模式会容易得多。

何时应该避免使用设计模式?

避免那些增加复杂性但并不能解决实际问题的模式。不要“以防万一”地引入模式。遵循 YAGNI:当且仅当存在明确且当前的问题时才添加模式。

我可以在函数式编程中使用设计模式吗?

可以。许多模式自然地映射到函数式技术。Strategy 可以用函数参数表示,Decorator 可以是高阶函数。关键是应用其背后的原则,而不是僵化地复制 OOP 形式。


在 Clean Code Guy,我们通过将基础原则嵌入团队工作流,帮助团队构建经久耐用的软件。了解我们的代码审计和面向 AI 的重构如何让你的团队自信交付,请访问 https://cleancodeguy.com

1.
W. G. Griswold 等,“A Formal Foundation for Design Pattern Abstraction and Reuse,”ECOOP ’93 会议录,加利福尼亚大学圣地亚哥分校。 https://cseweb.ucsd.edu/~wgg/CSE210/ecoop93-patterns.pdf
2.
Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides,《Design Patterns: Elements of Reusable Object-Oriented Software》(Addison-Wesley,1994)。 https://en.wikipedia.org/wiki/Design_Patterns
3.
Martin Fowler,“Singleton,”Bliki,讨论 Singleton 的权衡和测试影响。 https://martinfowler.com/bliki/Singleton.html
4.
Robert C. Martin(Uncle Bob),“The SOLID Principles,”解释开闭原则和相关设计目标。 https://8thlight.com/blog/uncle-bob/2012/08/13/the-s-o-l-i-d-principles.html
← Back to blog
🙋🏻‍♂️

AI编写代码。
您让它持久。

在AI加速的时代,干净代码不仅仅是好的实践 — 它是能够扩展的系统与在自己的重量下崩溃的代码库之间的区别。