设计模式实战:解锁代码复用与扩展的艺术
设计模式是软件工程中经过验证的、用于解决特定问题的通用解决方案。掌握设计模式不仅能帮助开发者编写更易于维护和扩展的代码,还能提升团队协作效率,建立统一的设计词汇。本文将深入剖析几种常用设计模式的核心原理、实现方式和实际应用场景,帮助开发者在实际工作中灵活运用这些"解决方案的模板"。
设计模式基础
设计模式的分类
根据《设计模式:可复用面向对象软件的基础》一书,设计模式可分为三大类:
- 创建型模式:关注对象的创建过程
- 结构型模式:关注类和对象的组合
- 行为型模式:关注对象间的通信和责任分配
设计模式的六大原则
设计模式应遵循以下设计原则:
- 单一职责原则:一个类只负责一项职责
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:子类可以替换父类并且不影响程序正确性
- 接口隔离原则:使用多个专门的接口比使用单一的总接口要好
- 依赖倒置原则:高层模块不应依赖低层模块,都应依赖抽象
- 迪米特法则:一个对象应尽可能少地了解其他对象
创建型模式详解
创建型模式封装了对象的创建过程,使系统独立于对象的创建、组合和表示方式。
单例模式
单例模式确保一个类只有一个实例,并提供全局访问点。
应用场景:
- 配置管理器
- 线程池
- 缓存
- 日志记录器
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
}
}
设计模式在实际项目中的应用
多模式协作:电子商务系统设计
以下是电子商务系统中多种设计模式协作的示例:
案例:Spring框架中的设计模式
Spring框架广泛使用了多种设计模式:
- 工厂模式:BeanFactory和ApplicationContext用于创建对象
- 单例模式:默认的Bean作用域是单例
- 代理模式:AOP实现中使用JDK动态代理或CGLIB
- 模板方法模式:JdbcTemplate、HibernateTemplate等
- 观察者模式: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;
}
);
}
}
设计模式反模式与误用
虽然设计模式提供了解决特定问题的通用方案,但不恰当的使用反而会增加复杂性。
常见误用场景
- 过度工程化:在简单场景使用复杂模式
- 模式迷恋:为使用模式而使用模式
- 错误选择:选择不适合问题的模式
- 泄露抽象:模式实现细节渗透到客户端代码
识别与避免误用的策略
- 先解决问题,后考虑模式:以问题为导向,而非以模式为导向
- 渐进式重构:从简单解决方案开始,需要时再重构为模式
- 团队评审:多人讨论设计决策
- 度量复杂性:使用模式前后对比代码复杂度变化
如何选择合适的设计模式
选择合适的设计模式是一项需要经验积累的技能。
设计模式决策树
实际指导原则
- 分析变化点:识别系统中最可能变化的部分
- 考虑扩展性需求:评估未来可能的扩展方向
- 权衡复杂性与收益:模式增加的复杂性是否值得
- 遵循原则而非教条:设计原则比特定模式更重要
总结与展望
设计模式是软件工程中解决特定问题的成熟方案,但并非银弹。合理使用设计模式可以提高代码质量、可维护性和可扩展性,但过度或不当使用则会适得其反。
随着软件开发范式的演进,设计模式也在不断发展:
- 函数式编程中的模式与传统面向对象模式有所不同
- 微服务架构引入了分布式系统设计模式
- 响应式编程带来了新的异步交互模式
作为开发者,我们应该将设计模式视为工具箱中的工具,而非教条。深入理解模式背后的设计原则,结合实际问题场景灵活应用,才能真正掌握设计模式的精髓。
参考资料
- github.com/iluwatar/ja... - Java设计模式示例集合
- refactoring.guru/design-patt... - 设计模式详解与代码示例
- sourcemaking.com/design_patt... - 设计模式实例解析
- springframework.guru/gang-of-fou... - Spring框架中的GOF设计模式
外宣传一下我们自己的产品:
面试准备利器「Offer蛙」:AI 驱动的智能面试助手,助你轻松拿下心仪 Offer。官网:mianshizhushou.com