设计模式实战:解锁代码复用与扩展的艺术

设计模式实战:解锁代码复用与扩展的艺术

设计模式是软件工程中经过验证的、用于解决特定问题的通用解决方案。掌握设计模式不仅能帮助开发者编写更易于维护和扩展的代码,还能提升团队协作效率,建立统一的设计词汇。本文将深入剖析几种常用设计模式的核心原理、实现方式和实际应用场景,帮助开发者在实际工作中灵活运用这些"解决方案的模板"。

设计模式基础

设计模式的分类

根据《设计模式:可复用面向对象软件的基础》一书,设计模式可分为三大类:

  1. 创建型模式:关注对象的创建过程
  2. 结构型模式:关注类和对象的组合
  3. 行为型模式:关注对象间的通信和责任分配
graph TD A[设计模式] --> B[创建型模式] A --> C[结构型模式] A --> D[行为型模式] B --> B1[单例模式] B --> B2[工厂方法] B --> B3[抽象工厂] B --> B4[建造者] B --> B5[原型模式] C --> C1[适配器] C --> C2[桥接模式] C --> C3[组合模式] C --> C4[装饰器] C --> C5[外观模式] C --> C6[享元模式] C --> C7[代理模式] D --> D1[责任链] D --> D2[命令模式] D --> D3[迭代器] D --> D4[观察者] D --> D5[策略模式] D --> D6[模板方法]

设计模式的六大原则

设计模式应遵循以下设计原则:

  1. 单一职责原则:一个类只负责一项职责
  2. 开闭原则:对扩展开放,对修改关闭
  3. 里氏替换原则:子类可以替换父类并且不影响程序正确性
  4. 接口隔离原则:使用多个专门的接口比使用单一的总接口要好
  5. 依赖倒置原则:高层模块不应依赖低层模块,都应依赖抽象
  6. 迪米特法则:一个对象应尽可能少地了解其他对象

创建型模式详解

创建型模式封装了对象的创建过程,使系统独立于对象的创建、组合和表示方式。

单例模式

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

应用场景

  • 配置管理器
  • 线程池
  • 缓存
  • 日志记录器

Java实现

java 复制代码
// 线程安全的懒汉式单例
public class Singleton {
    // volatile保证多线程环境下的可见性
    private static volatile Singleton instance;
    
    // 私有构造函数防止外部实例化
    private Singleton() {}
    
    public static Singleton getInstance() {
        // 双重检查锁定
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

JavaScript实现

javascript 复制代码
// ES6 Module自带单例特性
class DatabaseConnection {
    constructor() {
        this.connection = null;
    }
    
    connect() {
        if (!this.connection) {
            console.log('创建新数据库连接');
            this.connection = { status: 'connected' };
        }
        return this.connection;
    }
}

// 导出单例实例
export default new DatabaseConnection();

工厂方法模式

工厂方法模式定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。

应用场景

  • 不确定需要创建哪种对象
  • 创建过程涉及复杂逻辑
  • 需要统一管理对象创建

TypeScript实现

typescript 复制代码
// 产品接口
interface Product {
    operation(): string;
}

// 具体产品A
class ConcreteProductA implements Product {
    operation(): string {
        return "ConcreteProductA";
    }
}

// 具体产品B
class ConcreteProductB implements Product {
    operation(): string {
        return "ConcreteProductB";
    }
}

// 创建者抽象类
abstract class Creator {
    abstract factoryMethod(): Product;
    
    // 模板方法使用工厂方法
    someOperation(): string {
        const product = this.factoryMethod();
        return `Creator: ${product.operation()}`;
    }
}

// 具体创建者A
class ConcreteCreatorA extends Creator {
    factoryMethod(): Product {
        return new ConcreteProductA();
    }
}

// 具体创建者B
class ConcreteCreatorB extends Creator {
    factoryMethod(): Product {
        return new ConcreteProductB();
    }
}

// 客户端代码
function clientCode(creator: Creator) {
    console.log(creator.someOperation());
}

// 使用
clientCode(new ConcreteCreatorA()); // "Creator: ConcreteProductA"
clientCode(new ConcreteCreatorB()); // "Creator: ConcreteProductB"

建造者模式

建造者模式将复杂对象的构建过程分离出来,使相同的构建过程可以创建不同的表示。

应用场景

  • 构建复杂对象
  • 构建过程需要分步骤进行
  • 需要生成不同表示的对象

Python实现

python 复制代码
class Computer:
    def __init__(self):
        self.cpu = None
        self.memory = None
        self.storage = None
        self.gpu = None
    
    def __str__(self):
        return f"Computer [CPU: {self.cpu}, Memory: {self.memory}GB, " \
               f"Storage: {self.storage}GB, GPU: {self.gpu}]"


class ComputerBuilder:
    def __init__(self):
        self.computer = Computer()
    
    def configure_cpu(self, cpu):
        self.computer.cpu = cpu
        return self
    
    def configure_memory(self, memory):
        self.computer.memory = memory
        return self
    
    def configure_storage(self, storage):
        self.computer.storage = storage
        return self
    
    def configure_gpu(self, gpu):
        self.computer.gpu = gpu
        return self
    
    def build(self):
        return self.computer


class ComputerDirector:
    def build_gaming_pc(self, builder):
        return builder.configure_cpu("Intel i9") \
                     .configure_memory(32) \
                     .configure_storage(2000) \
                     .configure_gpu("RTX 3080") \
                     .build()
    
    def build_office_pc(self, builder):
        return builder.configure_cpu("Intel i5") \
                     .configure_memory(16) \
                     .configure_storage(512) \
                     .configure_gpu("Integrated") \
                     .build()


# 客户端代码
director = ComputerDirector()
builder = ComputerBuilder()

gaming_pc = director.build_gaming_pc(builder)
print(gaming_pc)  # Computer [CPU: Intel i9, Memory: 32GB, Storage: 2000GB, GPU: RTX 3080]

office_pc = director.build_office_pc(builder)
print(office_pc)  # Computer [CPU: Intel i5, Memory: 16GB, Storage: 512GB, GPU: Integrated]

结构型模式详解

结构型模式关注类和对象的组合,形成更大的结构,以实现新的功能。

适配器模式

适配器模式允许将一个类的接口转换成客户端所期望的另一个接口。

应用场景

  • 集成第三方库
  • 兼容旧系统接口
  • 统一接口规范

Go实现

go 复制代码
package main

import "fmt"

// 目标接口
type Target interface {
    Request() string
}

// 已有的类(不兼容目标接口)
type Adaptee struct{}

func (a *Adaptee) SpecificRequest() string {
    return "Adaptee: SpecificRequest"
}

// 适配器
type Adapter struct {
    adaptee *Adaptee
}

func NewAdapter(adaptee *Adaptee) *Adapter {
    return &Adapter{adaptee: adaptee}
}

// 实现Target接口
func (a *Adapter) Request() string {
    return fmt.Sprintf("Adapter: (Translated) %s", a.adaptee.SpecificRequest())
}

// 客户端代码
func main() {
    adaptee := &Adaptee{}
    adapter := NewAdapter(adaptee)
    
    fmt.Println(adapter.Request())
    // 输出: Adapter: (Translated) Adaptee: SpecificRequest
}

装饰器模式

装饰器模式动态地给对象添加额外的职责,是扩展功能的灵活替代方案。

应用场景

  • 在不修改原类的情况下添加功能
  • 动态组合多个功能
  • 避免类爆炸

TypeScript实现

typescript 复制代码
// 组件接口
interface Component {
    operation(): string;
}

// 具体组件
class ConcreteComponent implements Component {
    operation(): string {
        return "ConcreteComponent";
    }
}

// 装饰器基类
abstract class Decorator implements Component {
    protected component: Component;
    
    constructor(component: Component) {
        this.component = component;
    }
    
    operation(): string {
        return this.component.operation();
    }
}

// 具体装饰器A
class ConcreteDecoratorA extends Decorator {
    operation(): string {
        return `ConcreteDecoratorA(${super.operation()})`;
    }
    
    addedBehavior(): string {
        return "Added behavior A";
    }
}

// 具体装饰器B
class ConcreteDecoratorB extends Decorator {
    operation(): string {
        return `ConcreteDecoratorB(${super.operation()})`;
    }
    
    addedBehavior(): string {
        return "Added behavior B";
    }
}

// 客户端代码
const simple = new ConcreteComponent();
console.log("Client: Simple component:");
console.log(simple.operation());

// 装饰组件
const decorator1 = new ConcreteDecoratorA(simple);
const decorator2 = new ConcreteDecoratorB(decorator1);
console.log("Client: Decorated component:");
console.log(decorator2.operation());
// 输出: "ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))"

代理模式

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

应用场景

  • 延迟初始化(虚拟代理)
  • 访问控制(保护代理)
  • 远程服务调用(远程代理)
  • 缓存结果(缓存代理)

Java实现

java 复制代码
// 服务接口
interface Image {
    void display();
}

// 真实主题
class RealImage implements Image {
    private String filename;
    
    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }
    
    private void loadFromDisk() {
        System.out.println("Loading " + filename + " from disk");
    }
    
    @Override
    public void display() {
        System.out.println("Displaying " + filename);
    }
}

// 代理
class ProxyImage implements Image {
    private String filename;
    private RealImage realImage;
    
    public ProxyImage(String filename) {
        this.filename = filename;
    }
    
    @Override
    public void display() {
        // 延迟初始化
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.jpg");
        
        // 图像将从磁盘加载
        image.display();
        
        // 图像不会再次从磁盘加载
        image.display();
    }
}

行为型模式详解

行为型模式关注对象间的职责分配和算法抽象,使系统更灵活、更可复用。

观察者模式

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

应用场景

  • 事件处理系统
  • 消息推送服务
  • GUI界面更新
  • 状态监控

JavaScript实现

javascript 复制代码
// 主题/可观察者
class Subject {
    constructor() {
        this.observers = [];
    }
    
    // 添加观察者
    attach(observer) {
        if (!this.observers.includes(observer)) {
            this.observers.push(observer);
        }
    }
    
    // 移除观察者
    detach(observer) {
        const index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    }
    
    // 通知所有观察者
    notify(data) {
        for (const observer of this.observers) {
            observer.update(data);
        }
    }
}

// 具体主题
class WeatherStation extends Subject {
    constructor() {
        super();
        this.temperature = 0;
        this.humidity = 0;
    }
    
    // 设置测量值并通知观察者
    setMeasurements(temperature, humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.notify({ temperature, humidity });
    }
}

// 观察者接口
class Observer {
    update(data) {
        throw new Error('Observer.update() must be implemented');
    }
}

// 具体观察者
class DisplayElement extends Observer {
    constructor(name) {
        super();
        this.name = name;
    }
    
    update(data) {
        console.log(`${this.name} - Temperature: ${data.temperature}°C, Humidity: ${data.humidity}%`);
    }
}

// 客户端代码
const weatherStation = new WeatherStation();

const phoneDisplay = new DisplayElement('Phone App');
const desktopDisplay = new DisplayElement('Desktop Widget');

weatherStation.attach(phoneDisplay);
weatherStation.attach(desktopDisplay);

// 更新气象数据,观察者会收到通知
weatherStation.setMeasurements(25, 65);
// 输出:
// Phone App - Temperature: 25°C, Humidity: 65%
// Desktop Widget - Temperature: 25°C, Humidity: 65%

// 移除一个观察者
weatherStation.detach(phoneDisplay);

// 再次更新气象数据,只有剩余的观察者收到通知
weatherStation.setMeasurements(26, 70);
// 输出:
// Desktop Widget - Temperature: 26°C, Humidity: 70%

策略模式

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。

应用场景

  • 支付方式选择
  • 排序算法选择
  • 验证策略
  • 文件压缩算法选择

Python实现

python 复制代码
from abc import ABC, abstractmethod

# 策略接口
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

# 具体策略A
class CreditCardPayment(PaymentStrategy):
    def __init__(self, card_number, name, expiry, cvv):
        self.card_number = card_number
        self.name = name
        self.expiry = expiry
        self.cvv = cvv
    
    def pay(self, amount):
        print(f"Paid ${amount} using Credit Card ending with {self.card_number[-4:]}")
        return True

# 具体策略B
class PayPalPayment(PaymentStrategy):
    def __init__(self, email, password):
        self.email = email
        self.password = password
    
    def pay(self, amount):
        print(f"Paid ${amount} using PayPal account {self.email}")
        return True

# 具体策略C
class BankTransferPayment(PaymentStrategy):
    def __init__(self, account_number, bank_code):
        self.account_number = account_number
        self.bank_code = bank_code
    
    def pay(self, amount):
        print(f"Paid ${amount} using Bank Transfer from account {self.account_number}")
        return True

# 上下文
class ShoppingCart:
    def __init__(self):
        self.items = []
        self.payment_strategy = None
    
    def add_item(self, name, price):
        self.items.append({"name": name, "price": price})
    
    def calculate_total(self):
        return sum(item["price"] for item in self.items)
    
    def set_payment_strategy(self, payment_strategy):
        self.payment_strategy = payment_strategy
    
    def checkout(self):
        total = self.calculate_total()
        if self.payment_strategy is None:
            raise Exception("Payment strategy not set")
        
        return self.payment_strategy.pay(total)

# 客户端代码
cart = ShoppingCart()
cart.add_item("笔记本电脑", 1200)
cart.add_item("耳机", 100)

# 使用信用卡支付
cart.set_payment_strategy(CreditCardPayment("1234567890123456", "John Doe", "12/25", "123"))
cart.checkout()  # Paid $1300 using Credit Card ending with 3456

# 更换为PayPal支付
cart.set_payment_strategy(PayPalPayment("[email protected]", "password"))
cart.checkout()  # Paid $1300 using PayPal account [email protected]

命令模式

命令模式将请求封装为对象,使请求的发送者和接收者解耦。

应用场景

  • 队列请求
  • 操作日志
  • 事务处理
  • GUI命令和撤销操作

C#实现

csharp 复制代码
// 接收者
public class Light
{
    public void TurnOn()
    {
        Console.WriteLine("Light is on");
    }
    
    public void TurnOff()
    {
        Console.WriteLine("Light is off");
    }
}

// 命令接口
public interface ICommand
{
    void Execute();
    void Undo();
}

// 具体命令
public class LightOnCommand : ICommand
{
    private Light light;
    
    public LightOnCommand(Light light)
    {
        this.light = light;
    }
    
    public void Execute()
    {
        light.TurnOn();
    }
    
    public void Undo()
    {
        light.TurnOff();
    }
}

// 具体命令
public class LightOffCommand : ICommand
{
    private Light light;
    
    public LightOffCommand(Light light)
    {
        this.light = light;
    }
    
    public void Execute()
    {
        light.TurnOff();
    }
    
    public void Undo()
    {
        light.TurnOn();
    }
}

// 调用者
public class RemoteControl
{
    private ICommand onCommand;
    private ICommand offCommand;
    private ICommand undoCommand;
    
    public void SetCommand(ICommand onCommand, ICommand offCommand)
    {
        this.onCommand = onCommand;
        this.offCommand = offCommand;
    }
    
    public void PressOnButton()
    {
        onCommand.Execute();
        undoCommand = onCommand;
    }
    
    public void PressOffButton()
    {
        offCommand.Execute();
        undoCommand = offCommand;
    }
    
    public void PressUndoButton()
    {
        if (undoCommand != null)
        {
            undoCommand.Undo();
        }
    }
}

// 客户端代码
public class Program
{
    public static void Main()
    {
        // 创建接收者
        Light livingRoomLight = new Light();
        
        // 创建命令
        LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
        
        // 创建调用者
        RemoteControl remote = new RemoteControl();
        remote.SetCommand(livingRoomLightOn, livingRoomLightOff);
        
        // 按下按钮
        remote.PressOnButton();  // Light is on
        remote.PressOffButton(); // Light is off
        remote.PressUndoButton(); // Light is on
    }
}

设计模式在实际项目中的应用

多模式协作:电子商务系统设计

以下是电子商务系统中多种设计模式协作的示例:

graph TD A[电子商务系统] --> B[产品目录模块] A --> C[购物车模块] A --> D[支付模块] A --> E[订单模块] A --> F[用户模块] B --"工厂模式"--> B1[产品创建] B --"装饰器模式"--> B2[产品装饰] C --"单例模式"--> C1[购物车实例] C --"观察者模式"--> C2[价格变化通知] D --"策略模式"--> D1[支付方式] D --"适配器模式"--> D2[支付网关集成] E --"命令模式"--> E1[订单处理] E --"状态模式"--> E2[订单状态] F --"代理模式"--> F1[用户权限控制]

案例:Spring框架中的设计模式

Spring框架广泛使用了多种设计模式:

  1. 工厂模式:BeanFactory和ApplicationContext用于创建对象
  2. 单例模式:默认的Bean作用域是单例
  3. 代理模式:AOP实现中使用JDK动态代理或CGLIB
  4. 模板方法模式:JdbcTemplate、HibernateTemplate等
  5. 观察者模式:Spring事件机制通过ApplicationEvent和ApplicationListener实现
java 复制代码
// Spring中使用模板方法模式的例子
public class UserDao {
    private JdbcTemplate jdbcTemplate;
    
    public UserDao(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    
    public User findById(long id) {
        return jdbcTemplate.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            new Object[]{id},
            (rs, rowNum) -> {
                User user = new User();
                user.setId(rs.getLong("id"));
                user.setName(rs.getString("name"));
                user.setEmail(rs.getString("email"));
                return user;
            }
        );
    }
}

设计模式反模式与误用

虽然设计模式提供了解决特定问题的通用方案,但不恰当的使用反而会增加复杂性。

常见误用场景

  1. 过度工程化:在简单场景使用复杂模式
  2. 模式迷恋:为使用模式而使用模式
  3. 错误选择:选择不适合问题的模式
  4. 泄露抽象:模式实现细节渗透到客户端代码

识别与避免误用的策略

  1. 先解决问题,后考虑模式:以问题为导向,而非以模式为导向
  2. 渐进式重构:从简单解决方案开始,需要时再重构为模式
  3. 团队评审:多人讨论设计决策
  4. 度量复杂性:使用模式前后对比代码复杂度变化

如何选择合适的设计模式

选择合适的设计模式是一项需要经验积累的技能。

设计模式决策树

graph TD A[识别设计问题] --> B{创建对象?} B -->|是| C{创建单一实例?} C -->|是| D[单例模式] C -->|否| E{创建复杂对象?} E -->|是| F[建造者模式] E -->|否| G{创建对象家族?} G -->|是| H[抽象工厂] G -->|否| I[工厂方法] B -->|否| J{结构关系?} J -->|是| K{接口不兼容?} K -->|是| L[适配器模式] K -->|否| M{需要分离抽象和实现?} M -->|是| N[桥接模式] M -->|否| O{动态添加职责?} O -->|是| P[装饰器模式] O -->|否| Q[复合模式] B -->|否| R{对象行为?} R -->|是| S{状态变化通知多个对象?} S -->|是| T[观察者模式] S -->|否| U{算法可互换?} U -->|是| V[策略模式] U -->|否| W{解耦请求和处理?} W -->|是| X[命令模式] W -->|否| Y[其他行为模式]

实际指导原则

  1. 分析变化点:识别系统中最可能变化的部分
  2. 考虑扩展性需求:评估未来可能的扩展方向
  3. 权衡复杂性与收益:模式增加的复杂性是否值得
  4. 遵循原则而非教条:设计原则比特定模式更重要

总结与展望

设计模式是软件工程中解决特定问题的成熟方案,但并非银弹。合理使用设计模式可以提高代码质量、可维护性和可扩展性,但过度或不当使用则会适得其反。

随着软件开发范式的演进,设计模式也在不断发展:

  1. 函数式编程中的模式与传统面向对象模式有所不同
  2. 微服务架构引入了分布式系统设计模式
  3. 响应式编程带来了新的异步交互模式

作为开发者,我们应该将设计模式视为工具箱中的工具,而非教条。深入理解模式背后的设计原则,结合实际问题场景灵活应用,才能真正掌握设计模式的精髓。

参考资料

外宣传一下我们自己的产品:

面试准备利器「Offer蛙」:AI 驱动的智能面试助手,助你轻松拿下心仪 Offer。官网:mianshizhushou.com

相关推荐
听闻风很好吃1 分钟前
Java设计模式之观察者模式:从入门到架构级实践
java·观察者模式·设计模式
胎粉仔1 小时前
Swift —— delegate 设计模式
开发语言·设计模式·swift
十五年专注C++开发2 小时前
面试题:请描述一下你在项目中是如何进行性能优化的?针对哪些方面进行了优化,采取了哪些具体的措施?
开发语言·数据结构·c++·qt·设计模式·性能优化
小马爱打代码2 小时前
设计模式:命令模式-解耦请求与执行的完美方案
设计模式·命令模式
都叫我大帅哥2 小时前
代码界的击鼓传花:责任链模式的接力艺术
java·后端·设计模式
wenbin_java5 小时前
设计模式之状态模式:优雅管理对象行为变化
设计模式·状态模式
未定义.2216 小时前
UML-饮料自助销售系统(无法找零)序列图
设计模式·流程图·状态模式·软件工程·需求分析·uml
骊山道童7 小时前
设计模式-代理模式
设计模式
小马爱打代码7 小时前
设计模式:外观模式 - 简化复杂系统调用的利器
设计模式·外观模式