行为型模式(协作的艺术)
- 观察者模式
观察者模式详解
模式定义
观察者模式(Observer Pattern)是一种行为设计模式,用于建立对象间一对多的依赖关系。当一个对象(Subject)状态变化时,所有依赖它的对象(Observers)会自动收到通知并更新。该模式解耦了主题与观察者,遵循开闭原则(对扩展开放,对修改关闭)。
核心组件
- Subject (主题)
- 维护观察者列表(添加/删除方法)
- 提供状态更新通知方法(
NotifyObservers
)
- Observer (观察者接口)
- 定义更新接口(如
Update()
)
- 定义更新接口(如
- ConcreteObserver (具体观察者)
- 实现更新逻辑,与主题状态同步
- ConcreteSubject (具体主题)
- 存储具体状态,状态变更时触发通知
C# 实现示例
// 主题接口
public interface ISubject {
void RegisterObserver(IObserver o);
void RemoveObserver(IObserver o);
void NotifyObservers();
}
// 观察者接口
public interface IObserver {
void Update(string message);
}
// 具体主题(新闻发布中心)
public class NewsAgency : ISubject {
private List<IObserver> _observers = new List<IObserver>();
private string _latestNews;
public void RegisterObserver(IObserver o) => _observers.Add(o);
public void RemoveObserver(IObserver o) => _observers.Remove(o);
public void NotifyObservers() {
foreach (var observer in _observers) {
observer.Update(_latestNews);
}
}
public void PublishNews(string news) {
_latestNews = news;
NotifyObservers(); // 状态变更时自动通知
}
}
// 具体观察者(新闻订阅用户)
public class NewsSubscriber : IObserver {
private string _name;
public NewsSubscriber(string name) => _name = name;
public void Update(string news) {
Console.WriteLine($"{_name} received news: {news}");
}
}
// 使用
var agency = new NewsAgency();
var user1 = new NewsSubscriber("User1");
var user2 = new NewsSubscriber("User2");
agency.RegisterObserver(user1);
agency.RegisterObserver(user2);
agency.PublishNews("Breaking: New AI Model Released!");
Java 实现示例
import java.util.*;
// 主题接口
interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String message);
}
// 具体主题(股票交易所)
class StockExchange implements Subject {
private List<Observer> observers = new ArrayList<>();
private double stockPrice;
public void registerObserver(Observer o) { observers.add(o); }
public void removeObserver(Observer o) { observers.remove(o); }
public void notifyObservers() {
for (Observer o : observers) {
o.update("Price updated: " + stockPrice);
}
}
public void setStockPrice(double price) {
this.stockPrice = price;
notifyObservers(); // 价格变动时通知
}
}
// 具体观察者(交易员)
class Trader implements Observer {
private String name;
public Trader(String name) { this.name = name; }
@Override
public void update(String message) {
System.out.println(name + " notified: " + message);
}
}
// 使用
public class Main {
public static void main(String[] args) {
StockExchange exchange = new StockExchange();
Trader trader1 = new Trader("Alice");
Trader trader2 = new Trader("Bob");
exchange.registerObserver(trader1);
exchange.registerObserver(trader2);
exchange.setStockPrice(150.75);
}
}
常用应用场景
- 事件驱动系统
- GUI按钮点击事件(如C# WinForms、Java Swing)
- 游戏引擎中的碰撞检测事件
- 发布-订阅模型
- 新闻推送、社交媒体Feed更新
- 微服务间的事件通知(如Kafka消息队列)
- 状态监控
- 服务器资源使用率报警(CPU/Memory阈值触发)
- IoT设备数据实时仪表盘
- 数据同步
- 数据库变更同步到缓存(如Redis)
- Excel单元格修改联动图表刷新
- MVC架构
- 模型(Model)变更时自动更新视图(View)
关键注意事项
内存泄漏风险
- 问题:观察者未注销时,主题持有其引用导致无法GC回收。
- 解决:显式调用
RemoveObserver()
(如C#IDisposable
接口、Javaclose()
方法)。
通知顺序不可控
- 问题:观察者接收顺序依赖注册顺序,可能影响业务逻辑。
- 解决:引入优先级机制或保证观察者独立性。
性能瓶颈
- 问题:观察者过多或更新逻辑复杂导致通知延迟。
- 解决:
- 异步通知(如C#
Task.Run
、JavaExecutorService
) - 批量更新(合并多次状态变更)
- 异步通知(如C#
循环依赖
- 问题:观察者更新时反向修改主题状态,引发递归通知。
- 解决:状态变更前检查
if (oldState != newState)
。
线程安全问题
- 问题:多线程环境下观察者列表动态修改导致
ConcurrentModificationException
(Java)。 - 解决:使用线程安全集合(如
CopyOnWriteArrayList
)或同步锁。
- 问题:多线程环境下观察者列表动态修改导致
架构设计建议
使用现有框架
- C#:内置
IObservable<T>
/IObserver<T>
接口public class Stock : IObservable<double> { private List<IObserver<double>> observers = new List<IObserver<double>>(); public IDisposable Subscribe(IObserver<double> observer) { ... } }
- Java:
java.util.Observable
类(已过时,推荐自定义实现)
- C#:内置
事件与委托优化(C#专属)
- 使用
event
关键字简化观察者管理:public class Publisher { public event Action<string> OnMessagePublished; public void Publish(string msg) => OnMessagePublished?.Invoke(msg); }
- 使用
避免过度耦合
- 观察者不应直接调用具体主题的方法,通过接口交互。
- 主题通过
NotifyObservers()
传递最小必要数据(如事件对象而非整个主题引用)。
分布式场景扩展
- 结合消息中间件(如RabbitMQ、Azure Service Bus)实现跨进程观察者模式。
测试策略
- 使用Mock观察者验证主题通知逻辑。
- 注入虚拟主题测试观察者行为。
模式优缺点
优点 | 缺点 |
---|---|
主题与观察者解耦 | 意外更新链(级联通知) |
动态添加/移除观察者 | 调试困难(间接调用链) |
符合开闭原则(新增观察者无需改主题) | 可能增加系统复杂性 |
适用性:适合状态变化触发跨模块更新,但需警惕过度使用导致系统难以追踪。
- 策略模式
策略模式详解:架构师视角
策略模式(Strategy Pattern)是一种行为设计模式,它定义一系列算法,封装每个算法,并使它们可以相互替换。策略模式让算法的变化独立于使用它的客户端。
核心结构
classDiagram
class Context {
-IStrategy strategy
+SetStrategy(IStrategy)
+ExecuteStrategy()
}
interface IStrategy {
+Execute()
}
class ConcreteStrategyA {
+Execute()
}
class ConcreteStrategyB {
+Execute()
}
Context o--> IStrategy
IStrategy <|.. ConcreteStrategyA
IStrategy <|.. ConcreteStrategyB
Context(上下文):
- 维护策略对象的引用
- 提供设置策略的方法
- 执行策略的入口
Strategy(策略接口):
- 定义算法族的公共接口
ConcreteStrategy(具体策略):
- 实现策略接口的具体算法
C#/Java 代码示例
C# 实现
// 策略接口
public interface IPaymentStrategy
{
void ProcessPayment(decimal amount);
}
// 具体策略
public class CreditCardPayment : IPaymentStrategy
{
public void ProcessPayment(decimal amount)
=> Console.WriteLine($"Credit card: ${amount}");
}
public class PayPalPayment : IPaymentStrategy
{
public void ProcessPayment(decimal amount)
=> Console.WriteLine($"PayPal: ${amount}");
}
// 上下文
public class PaymentContext
{
private IPaymentStrategy _strategy;
public void SetStrategy(IPaymentStrategy strategy)
=> _strategy = strategy;
public void ExecutePayment(decimal amount)
=> _strategy?.ProcessPayment(amount);
}
// 使用
var context = new PaymentContext();
context.SetStrategy(new CreditCardPayment());
context.ExecutePayment(100); // 输出: Credit card: $100
Java 实现
// 策略接口
interface PaymentStrategy {
void processPayment(BigDecimal amount);
}
// 具体策略
class WeChatPay implements PaymentStrategy {
@Override
public void processPayment(BigDecimal amount) {
System.out.println("WeChat Pay: " + amount);
}
}
class Alipay implements PaymentStrategy {
@Override
public void processPayment(BigDecimal amount) {
System.out.println("Alipay: " + amount);
}
}
// 上下文
class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(BigDecimal amount) {
if(strategy != null) {
strategy.processPayment(amount);
}
}
}
// 使用
PaymentContext context = new PaymentContext();
context.setStrategy(new Alipay());
context.executePayment(new BigDecimal("200")); // 输出: Alipay: 200
典型应用场景
支付系统(如示例):
- 支持多种支付方式(信用卡/PayPal/加密货币)
- 新增支付方式不影响现有逻辑
数据压缩/加密:
public interface ICompressionStrategy { byte[] Compress(byte[] data); } public class ZipCompression : ICompressionStrategy { ... } public class RarCompression : ICompressionStrategy { ... }
路由算法:
public interface IRoutingStrategy { Route CalculateRoute(Point start, Point end); } public class FastestRouteStrategy implements IRoutingStrategy { ... } public class EcoRouteStrategy implements IRoutingStrategy { ... }
表单验证:
public interface IValidationStrategy { bool Validate(string input); } public class EmailValidation : IValidationStrategy { ... } public class PhoneValidation : IValidationStrategy { ... }
电商折扣策略:
public interface IDiscountStrategy { BigDecimal applyDiscount(BigDecimal originalPrice); } public class VIPDiscount implements IDiscountStrategy { ... } public class FestivalDiscount implements IDiscountStrategy { ... }
架构建议与注意事项
避免策略膨胀:
- 当策略超过10个时,考虑使用策略工厂管理创建
public class StrategyFactory { public IPaymentStrategy Create(string type) => type switch { "CreditCard" => new CreditCardPayment(), "PayPal" => new PayPalPayment(), _ => throw new ArgumentException() }; }
策略与状态模式区分:
- 策略模式:算法选择(客户端主动切换)
- 状态模式:状态驱动(自动切换)
性能优化:
- 无状态策略可设计为单例减少对象创建
public enum CompressionSingleton implements ICompressionStrategy { INSTANCE; @Override public byte[] compress(byte[] data) { ... } }
依赖注入集成:
- 通过DI容器管理策略生命周期
services.AddTransient<IPaymentStrategy, CreditCardPayment>(); services.AddTransient<IPaymentStrategy, PayPalPayment>();
策略组合:
- 支持策略的嵌套组合(装饰器模式)
public class DiscountCombo implements IDiscountStrategy { private List<IDiscountStrategy> strategies; public BigDecimal applyDiscount(BigDecimal price) { for (IDiscountStrategy s : strategies) { price = s.applyDiscount(price); } return price; } }
文档规范:
- 使用XML注释/JavaDoc明确策略契约
/** * @implSpec 实现必须保证线程安全 */ public interface ICachingStrategy { ... }
反模式警示
策略泄露:
// 错误:上下文暴露策略细节 public class PaymentContext { public void ProcessCreditCard(CardDetails card) { ... } }
过度抽象:
- 简单条件判断无需策略模式
// 不推荐:只有两种固定策略 if(isVip) { new VIPDiscount().apply(price); } else { new RegularDiscount().apply(price); }
策略耦合:
// 错误:策略依赖具体实现 public class Alipay : IPaymentStrategy { public void Process() { // 直接依赖微信支付 new WeChatPay().Process(); } }
性能考量
场景 | 建议方案 |
---|---|
高频调用策略 | 使用轻量级策略对象 |
策略初始化成本高 | 延迟加载/Lazy初始化 |
需要动态切换策略 | 使用享元模式管理策略实例 |
策略执行需要上下文数据 | 通过参数传递避免状态存储 |
最佳实践总结
- 定义清晰策略边界:每个策略应解决单一问题
- 面向接口编程:上下文仅依赖策略接口
- 控制策略范围:避免创建过于细粒度的策略
- 结合工厂模式:解耦策略创建逻辑
- 单元测试策略:独立测试每个策略实现
- 监控策略执行:记录策略使用情况用于分析
架构师洞见:策略模式本质是将可变性封装在独立的策略对象中,符合开闭原则(OCP)。在微服务架构中,可将复杂策略实现为独立服务,通过策略网关进行路由,实现架构级策略管理。
- 命令模式
命令模式详解
命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为独立对象(命令),解耦请求的发送者(Invoker)和接收者(Receiver),支持请求排队、日志记录、撤销/重做等操作。
核心组件
角色 | 作用 |
---|---|
Command | 抽象命令接口,声明执行方法(如 Execute() ) |
ConcreteCommand | 具体命令,绑定接收者与动作,实现 Execute() (调用接收者的方法) |
Receiver | 实际执行业务逻辑的对象(如数据库操作、设备控制) |
Invoker | 触发命令的对象(如按钮、菜单),不直接依赖接收者 |
Client | 创建命令对象并设置其接收者 |
C# 实现示例
// 命令接口
public interface ICommand {
void Execute();
void Undo(); // 支持撤销
}
// 接收者:实际执行操作的对象
public class Light {
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}
// 具体命令
public class LightOnCommand : ICommand {
private Light _light;
public LightOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
public void Undo() => _light.TurnOff(); // 撤销操作
}
// 调用者(如遥控器按钮)
public class RemoteControl {
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() {
_command.Execute();
_history.Push(_command); // 记录历史用于撤销
}
private Stack<ICommand> _history = new Stack<ICommand>();
}
// 客户端
var light = new Light();
var lightOn = new LightOnCommand(light);
var remote = new RemoteControl();
remote.SetCommand(lightOn);
remote.PressButton(); // 输出:"Light is ON"
Java 实现示例
// 命令接口
interface Command {
void execute();
void undo();
}
// 接收者
class Light {
public void turnOn() { System.out.println("Light ON"); }
public void turnOff() { System.out.println("Light OFF"); }
}
// 具体命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
@Override
public void execute() { light.turnOn(); }
@Override
public void undo() { light.turnOff(); }
}
// 调用者
class RemoteControl {
private Command command;
private Stack<Command> history = new Stack<>();
public void setCommand(Command command) { this.command = command; }
public void pressButton() {
command.execute();
history.push(command);
}
}
// 客户端
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // 输出:"Light ON"
}
常用应用场景
- GUI 操作
- 按钮点击、菜单命令(如文本编辑器的复制/粘贴)
- 示例:将每个操作封装为命令对象,支持撤销/重做。
- 任务队列与日志
- 将请求排队(如线程池任务调度)
- 记录操作日志(用于审计或崩溃恢复)
- 事务行为
- 数据库事务的原子操作(失败时执行反向命令回滚)
- 宏命令(组合命令)
- 一键执行多个命令(如批量处理文件)
- 异步任务管理
- 将耗时操作封装为命令,由后台线程执行
注意事项与架构建议
注意事项
- 避免过度设计:简单请求(如直接函数调用)无需使用命令模式。
- 内存消耗:每个命令都是一个独立对象,大量命令可能增加内存开销。
- 撤销实现复杂性:
- 需存储额外状态(如
Memento
模式)或反向操作逻辑。 - 多次撤销可能需维护命令历史栈。
- 需存储额外状态(如
架构建议
- 结合其他模式增强能力
- 组合模式:实现宏命令(组合多个子命令)。
- 备忘录模式:存储命令执行前的状态,支持完美撤销。
// 示例:宏命令(C#) public class MacroCommand : ICommand { private List<ICommand> _commands = new List<ICommand>(); public void Add(ICommand cmd) => _commands.Add(cmd); public void Execute() => _commands.ForEach(cmd => cmd.Execute()); }
- 依赖注入
- 通过 DI 容器管理命令和接收者,提高可测试性。
- 线程安全
- 多线程环境下,命令对象需设计为无状态或使用锁机制。
- 空命令对象
- 提供
NullCommand
避免 Invoker 对命令的空值检查。
// Java 空命令示例 class NullCommand implements Command { @Override public void execute() {} @Override public void undo() {} }
- 提供
- 命令持久化
- 序列化命令对象,支持重启后恢复操作(如游戏存档)。
设计原则验证
- 单一职责原则:命令对象仅封装请求,接收者专注业务逻辑。
- 开闭原则:新增命令无需修改 Invoker 或 Receiver。
- 松耦合:Invoker 仅依赖抽象命令,与接收者解耦。
总结
适用场景:需要解耦请求发送者与接收者、支持撤销/重做、任务队列或日志的场景。
避坑指南:
- 简单场景避免滥用,优先考虑函数式编程替代(如 C#
Action
/JavaRunnable
)。 - 为高频命令实现轻量级版本(如共享无状态接收者)。
- 撤销功能需在早期设计阶段考虑,避免后期重构。
命令模式通过封装请求为对象,显著提升系统的灵活性与扩展性,是复杂操作管理的核心模式之一。
- 状态模式
状态模式(State Pattern)详解
核心概念
状态模式是一种行为型设计模式,允许对象在内部状态改变时改变其行为,使对象看起来像是修改了它的类。该模式将状态相关的行为封装到独立的类中,并通过委托机制让上下文对象在不同状态下表现出不同行为。
核心组件
- Context(上下文)
- 持有当前状态对象的引用
- 定义客户端交互接口
- 提供状态切换方法
- State(抽象状态)
- 定义状态接口,声明状态相关行为
- ConcreteState(具体状态)
- 实现状态接口,封装特定状态下的行为
C# 实现示例
// 抽象状态
public interface IOrderState
{
void Confirm(OrderContext context);
void Cancel(OrderContext context);
void Ship(OrderContext context);
}
// 具体状态:待确认
public class PendingState : IOrderState
{
public void Confirm(OrderContext context)
=> context.SetState(new ConfirmedState());
public void Cancel(OrderContext context)
=> context.SetState(new CancelledState());
public void Ship(OrderContext context)
=> throw new InvalidOperationException("待确认订单不能发货");
}
// 具体状态:已确认
public class ConfirmedState : IOrderState
{
public void Confirm(OrderContext context)
=> throw new InvalidOperationException("订单已确认");
public void Cancel(OrderContext context)
=> context.SetState(new CancelledState());
public void Ship(OrderContext context)
=> context.SetState(new ShippedState());
}
// 上下文
public class OrderContext
{
private IOrderState _state = new PendingState();
public void SetState(IOrderState state) => _state = state;
public void Confirm() => _state.Confirm(this);
public void Cancel() => _state.Cancel(this);
public void Ship() => _state.Ship(this);
}
// 使用
var order = new OrderContext();
order.Confirm(); // 状态转为ConfirmedState
order.Ship(); // 状态转为ShippedState
Java 实现示例
// 抽象状态
interface OrderState {
void confirm(OrderContext context);
void cancel(OrderContext context);
void ship(OrderContext context);
}
// 具体状态:待确认
class PendingState implements OrderState {
public void confirm(OrderContext context) {
context.setState(new ConfirmedState());
}
public void cancel(OrderContext context) {
context.setState(new CancelledState());
}
public void ship(OrderContext context) {
throw new IllegalStateException("待确认订单不能发货");
}
}
// 上下文
class OrderContext {
private OrderState state = new PendingState();
void setState(OrderState state) { this.state = state; }
void confirm() { state.confirm(this); }
void cancel() { state.cancel(this); }
void ship() { state.ship(this); }
}
典型应用场景
订单/工作流系统
- 订单状态(待支付/已发货/已完成)
- 审批流程(草稿/审批中/已批准)
游戏角色状态
- 角色行为(站立/奔跑/跳跃/攻击)
- 状态切换时行为变化(如跳跃时不可攻击)
硬件控制
- 电梯状态(停止/运行/故障)
- 打印机状态(空闲/打印/卡纸)
UI 组件交互
- 按钮状态(正常/禁用/悬停)
- 动画控件状态(开始/暂停/停止)
网络协议处理
- TCP连接状态(建立连接/数据传输/断开连接)
- 协议状态机实现
注意事项
避免状态膨胀
- 当状态超过10个时需重新评估设计
- 考虑使用状态表(State Table)或状态机库替代
状态转换控制
- 转换逻辑放在Context还是State?
- Context控制:集中管理转换规则(推荐)
- State控制:增加状态间耦合(慎用)
- 转换逻辑放在Context还是State?
状态共享
- 无内部状态的状态对象可设计为单例
public class CancelledState : IOrderState { public static readonly IOrderState Instance = new CancelledState(); // ... 实现方法 }
初始化问题
- 确保Context初始状态有效
- 使用工厂方法初始化默认状态
并发安全
- 多线程环境下需同步状态切换
- 推荐使用不可变状态对象
架构建议
1. 分层状态机
- 复杂场景使用分层状态(HFSM)
- 子状态可继承父状态行为
2. 与策略模式对比
状态模式 | 策略模式 |
---|---|
状态自动转换 | 策略手动切换 |
状态知晓上下文 | 策略独立于上下文 |
状态间存在关联 | 策略可独立替换 |
3. 性能优化
- 状态预创建:初始化时创建所有状态对象
- 轻量状态:避免在状态中存储上下文数据
4. 测试建议
- 为每个状态类编写独立单元测试
- 验证非法状态转换的异常处理
- 使用Mock对象测试状态间交互
5. 与其它模式协作
- 结合享元模式:共享无状态的状态对象
- 使用备忘录模式:实现状态历史回溯
- 通过观察者模式:通知状态变更事件
反模式警示
上帝状态对象
// 错误示范:一个状态类处理所有逻辑 public class GodState : IOrderState { public void Handle(OrderContext ctx) { if(ctx.Status == Status.Pending) {...} else if(ctx.Status == Status.Confirmed) {...} // 违反开闭原则 } }
循环依赖
- 状态类不应直接引用其它具体状态类
- 通过Context进行状态切换解耦
忽略状态重置
- 长时间运行的系统需设计状态重置机制
- 提供
Reset()
方法恢复初始状态
最佳实践总结
- 明确状态边界:每个状态对应唯一行为集合
- 单向依赖原则:状态类只依赖抽象Context
- 有限状态转换:使用状态转换图定义合法路径
- 文档驱动:使用PlantUML等工具维护状态图
- 防御式编程:在Context中验证状态转换合法性
架构师建议:在核心业务逻辑(如订单/支付)中使用状态模式,能显著提升系统可维护性。对于超复杂状态机(>20状态),推荐使用专门的State Machine框架(如Stateless for .NET或Spring State Machine)。
- 责任链
责任链模式详解
模式定义
责任链(Chain of Responsibility)是一种行为型设计模式,允许多个对象按顺序处理请求,形成处理链。请求沿链传递直到被处理或到达链尾,实现发送者与接收者的解耦。
核心组件
- Handler(抽象处理者)
- 定义处理请求的接口
- 持有下一个处理者的引用(可选)
- ConcreteHandler(具体处理者)
- 实现请求处理逻辑
- 决定是否处理请求或传递给下一处理者
- Request(请求对象)
- 封装请求数据
C# 实现示例
// 请求类
public class PurchaseRequest {
public decimal Amount { get; set; }
}
// 抽象处理者
public abstract class Approver {
protected Approver? NextApprover;
public void SetNext(Approver next) => NextApprover = next;
public abstract void Process(PurchaseRequest request);
}
// 具体处理者:经理
public class Manager : Approver {
public override void Process(PurchaseRequest request) {
if (request.Amount <= 1000) {
Console.WriteLine($"经理审批: {request.Amount}元");
} else if (NextApprover != null) {
NextApprover.Process(request); // 传递请求
}
}
}
// 具体处理者:总监
public class Director : Approver {
public override void Process(PurchaseRequest request) {
if (request.Amount <= 5000) {
Console.WriteLine($"总监审批: {request.Amount}元");
} else if (NextApprover != null) {
NextApprover.Process(request);
}
}
}
// 使用
var manager = new Manager();
var director = new Director();
manager.SetNext(director);
manager.Process(new PurchaseRequest { Amount = 800 }); // 经理处理
manager.Process(new PurchaseRequest { Amount = 4500 }); // 总监处理
Java 实现示例
// 抽象处理者
abstract class Approver {
protected Approver next;
public void setNext(Approver next) { this.next = next; }
public abstract void process(PurchaseRequest request);
}
// 具体处理者
class Manager extends Approver {
@Override
public void process(PurchaseRequest request) {
if (request.getAmount() <= 1000) {
System.out.println("经理审批: " + request.getAmount());
} else if (next != null) {
next.process(request); // 传递请求
}
}
}
// 使用
Approver manager = new Manager();
Approver director = new Director();
manager.setNext(director);
manager.process(new PurchaseRequest(800)); // 经理处理
manager.process(new PurchaseRequest(4500)); // 总监处理
常用场景
- 多级审批系统
- 费用报销、请假审批(不同金额/时长由不同层级审批)
- 请求过滤/中间件
- Web框架的过滤器链(如Spring Security)
- 日志记录、身份验证、数据清洗
- 事件处理
- GUI事件冒泡(如Java AWT/Swing)
- 异常处理
- 多级异常捕获机制(如Java的try-catch链)
- 日志分级处理
- DEBUG → INFO → WARN → ERROR 链式传递
注意事项
- 链的终止条件
- 必须确保链有终点,避免请求无限循环
- 可在基类添加默认处理逻辑(如抛出异常或日志警告)
- 性能考量
- 长链可能导致性能下降,避免高频场景使用
- 调试复杂性
- 请求传递路径不易跟踪,需完善日志
- 处理者顺序敏感
- 链的顺序影响行为(如权限检查应先于业务处理)
- 请求丢失风险
- 确保所有处理者正确传递未处理的请求
架构建议
- 动态链构建
- 使用配置文件或依赖注入动态组装链(如Spring的
@Order
)
// Spring示例:按Order注解排序 @Component @Order(1) class AuthFilter implements Filter { ... }
- 使用配置文件或依赖注入动态组装链(如Spring的
- 组合模式融合
- 复杂场景下,用组合模式构建树形责任链
- 模板方法优化
- 在抽象类中封装传递逻辑,子类聚焦核心处理
public abstract class Approver { protected virtual bool CanHandle(PurchaseRequest r) => false; public void Process(PurchaseRequest r) { if (CanHandle(r)) Handle(r); else NextApprover?.Process(r); } protected abstract void Handle(PurchaseRequest r); }
- 短路机制
- 特定场景可中断传递(如权限验证失败时直接返回)
- 监控扩展
- 添加链执行统计(如处理时长、成功率)
经典应用
框架/系统 | 应用场景 |
---|---|
Spring MVC | 拦截器链(HandlerInterceptor) |
Servlet Filter | 请求过滤链 |
Log4j/Logback | 日志级别过滤链 |
Netty | ChannelHandler处理链 |
责任链模式通过解耦请求发送者和处理者,显著提升系统扩展性。适用于需动态调整处理流程的场景,但需谨慎处理链的终止条件和性能影响。
15. 迭代器
迭代器模式详解(Iterator Pattern)
模式定义
迭代器模式是一种行为设计模式,提供一种方法顺序访问聚合对象中的各个元素,而无需暴露该对象的内部表示。它通过将遍历逻辑从聚合对象中分离,实现“单一职责原则”,同时支持多种遍历方式。
核心组件
组件 | 作用 | 示例接口 |
---|---|---|
迭代器接口 | 定义遍历操作(如 next, hasNext) | IEnumerator (C#) / Iterator (Java) |
具体迭代器 | 实现迭代器接口,持有聚合对象的引用并跟踪遍历位置 | ListEnumerator |
聚合接口 | 定义创建迭代器的方法 | IEnumerable (C#) / Iterable (Java) |
具体聚合类 | 实现聚合接口,返回具体迭代器实例 | List<T> |
C# 实现示例
// 迭代器接口
public interface IIterator
{
bool HasNext();
object Next();
}
// 聚合接口
public interface IAggregate
{
IIterator CreateIterator();
}
// 具体聚合类
public class ConcreteAggregate : IAggregate
{
private object[] _items = { "A", "B", "C" };
public IIterator CreateIterator()
{
return new ConcreteIterator(this);
}
public int Count => _items.Length;
public object this[int index] => _items[index];
}
// 具体迭代器
public class ConcreteIterator : IIterator
{
private readonly ConcreteAggregate _aggregate;
private int _index = 0;
public ConcreteIterator(ConcreteAggregate aggregate)
{
_aggregate = aggregate;
}
public bool HasNext() => _index < _aggregate.Count;
public object Next() => _aggregate[_index++];
}
// 使用
var aggregate = new ConcreteAggregate();
var iterator = aggregate.CreateIterator();
while (iterator.HasNext())
{
Console.WriteLine(iterator.Next()); // 输出 A, B, C
}
Java 实现示例
// 迭代器接口
public interface Iterator<T> {
boolean hasNext();
T next();
}
// 聚合接口
public interface Iterable<T> {
Iterator<T> createIterator();
}
// 具体聚合类
public class ConcreteAggregate implements Iterable<String> {
private String[] items = {"A", "B", "C"};
@Override
public Iterator<String> createIterator() {
return new ConcreteIterator();
}
private class ConcreteIterator implements Iterator<String> {
private int index = 0;
@Override
public boolean hasNext() {
return index < items.length;
}
@Override
public String next() {
return items[index++];
}
}
}
// 使用
ConcreteAggregate aggregate = new ConcreteAggregate();
Iterator<String> iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next()); // 输出 A, B, C
}
常用场景
遍历复杂数据结构
- 树形结构(二叉树、多叉树)
- 图结构(DFS/BFS遍历器)
- 自定义集合(如分页查询结果集)
解耦客户端与集合实现
- 客户端仅依赖迭代器接口,不依赖具体集合类(如
List
vsSet
) - 示例:统一遍历数据库查询结果与内存集合
- 客户端仅依赖迭代器接口,不依赖具体集合类(如
延迟加载/流式处理
- 大数据集分批加载(如数据库分页迭代器)
- 流处理管道(如 Java Stream API 的迭代器实现)
多线程安全遍历
- 提供快照迭代器(如
CopyOnWriteArrayList
) - 并发修改检测(Java 的
ConcurrentModificationException
)
- 提供快照迭代器(如
注意事项
线程安全问题
- 迭代过程中修改集合会导致未定义行为(如 Java 的
fail-fast
机制) - 解决方案:使用并发集合或克隆数据
- 迭代过程中修改集合会导致未定义行为(如 Java 的
资源泄漏风险
- 迭代器持有集合引用可能导致内存泄漏
- 特别在长生命周期迭代器(如缓存中)需注意
性能开销
- 每步操作需状态检查(如
hasNext()
) - 复杂数据结构(如树)的迭代器实现可能低效
- 每步操作需状态检查(如
空迭代器处理
- 空集合应返回有效的“空迭代器”,而非
null
- 空集合应返回有效的“空迭代器”,而非
架构设计建议
优先使用语言内置迭代器
- C#:
IEnumerable<T>
+yield return
public IEnumerable<int> GetValues() { for (int i = 0; i < 10; i++) { yield return i; // 自动生成迭代器 } }
- Java:实现
Iterable<T>
+ LambdaList<Integer> list = Arrays.asList(1, 2, 3); list.forEach(System.out::println); // 内部迭代
- C#:
为自定义集合提供标准迭代器
- 实现语言标准接口(如 Java 的
Iterable
),方便与现有库集成 - 示例:自定义树结构实现
Iterable<Node>
- 实现语言标准接口(如 Java 的
支持多种遍历策略
- 通过工厂方法返回不同迭代器:
public Iterator<T> createIterator(TraversalType type) { switch(type) { case IN_ORDER: return new InOrderIterator(); case PRE_ORDER: return new PreOrderIterator(); } }
- 通过工厂方法返回不同迭代器:
防御性编程
- 迭代器实现应检测并发修改:
public class SafeIterator { private final int expectedModCount; public T next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); // ... } }
- 迭代器实现应检测并发修改:
避免暴露内部状态
- 迭代器不应提供修改集合的方法(如
remove()
需谨慎设计) - 分离只读迭代器与读写迭代器接口
- 迭代器不应提供修改集合的方法(如
典型应用案例
集合框架
Java 的ArrayList.iterator()
, C# 的List<T>.GetEnumerator()
数据库访问
ORM 框架的查询结果迭代(如 Entity Framework 的IQueryable
)文件/网络流处理
按行读取大文件:foreach (var line in File.ReadLines("large.txt")) { // 逐行处理,避免全量加载 }
组合模式树遍历
递归遍历 UI 组件树:public class UIComponent implements Iterable<UIComponent> { private List<UIComponent> children = new ArrayList<>(); public Iterator<UIComponent> iterator() { return new DepthFirstIterator(this); } }
总结
迭代器模式通过抽象遍历过程,实现了:
- 解耦:分离集合结构与遍历逻辑
- 扩展性:支持多种遍历方式且不修改聚合类
- 简洁性:统一使用
foreach
等语法糖
黄金法则:当你的集合需要提供遍历能力时,优先实现语言标准迭代协议(C# 的 IEnumerable
/Java 的 Iterable
),而非自定义接口。这能最大化兼容性和可维护性。
16. 中介者
中介者模式(Mediator Pattern)详解
中介者模式是一种行为设计模式,通过引入中介对象封装一组对象之间的交互,降低对象间的直接耦合。核心思想是 “对象间不直接通信,而是通过中介者转发请求”。
模式结构
C# 示例:智能家居控制系统
// 中介者接口
public interface ISmartHomeMediator
{
void Register(Device device);
void Notify(Device sender, string eventType);
}
// 具体中介者
public class HomeAutomationHub : ISmartHomeMediator
{
private readonly List<Device> _devices = new();
public void Register(Device device) => _devices.Add(device);
public void Notify(Device sender, string eventType)
{
foreach (var device in _devices.Where(d => d != sender))
{
device.HandleEvent(eventType);
}
}
}
// 抽象同事类
public abstract class Device
{
protected ISmartHomeMediator Mediator;
public void SetMediator(ISmartHomeMediator mediator) => Mediator = mediator;
public abstract void HandleEvent(string eventType);
}
// 具体设备
public class Light : Device
{
public override void HandleEvent(string eventType)
{
if (eventType == "MOTION_DETECTED")
Console.WriteLine("Light turns ON");
else if (eventType == "NO_MOTION")
Console.WriteLine("Light turns OFF");
}
}
public class SecurityCamera : Device
{
public void DetectMotion() => Mediator.Notify(this, "MOTION_DETECTED");
}
// 使用
var hub = new HomeAutomationHub();
var light = new Light();
var camera = new SecurityCamera();
light.SetMediator(hub);
camera.SetMediator(hub);
hub.Register(light);
hub.Register(camera);
camera.DetectMotion(); // 触发所有设备响应
Java 示例:航空交通管制系统
// 中介者接口
interface AirTrafficControl {
void registerFlight(Flight flight);
void sendWarning(Flight sender, String message);
}
// 具体中介者
class ControlTower implements AirTrafficControl {
private List<Flight> flights = new ArrayList<>();
public void registerFlight(Flight flight) {
flights.add(flight);
}
public void sendWarning(Flight sender, String message) {
for (Flight flight : flights) {
if (flight != sender) {
flight.receiveWarning(message);
}
}
}
}
// 抽象同事类
abstract class Flight {
protected AirTrafficControl atc;
public void setATC(AirTrafficControl atc) {
this.atc = atc;
}
public abstract void receiveWarning(String message);
}
// 具体航班
class CargoFlight extends Flight {
public void reportPosition() {
atc.sendWarning(this, "CARGO_FLIGHT at 10,000m");
}
@Override
public void receiveWarning(String message) {
System.out.println("Cargo flight received: " + message);
}
}
// 使用
ControlTower tower = new ControlTower();
CargoFlight flight1 = new CargoFlight();
CargoFlight flight2 = new CargoFlight();
flight1.setATC(tower);
flight2.setATC(tower);
tower.registerFlight(flight1);
tower.registerFlight(flight2);
flight1.reportPosition(); // 触发其他航班接收警告
常用应用场景
- GUI组件交互
- 按钮/文本框/下拉菜单通过中介者协调(如:表单验证、状态联动)
- 分布式系统协调
- 微服务通过消息中介(如RabbitMQ)解耦通信
- 游戏开发
- 角色/道具/环境通过游戏主循环中介交互
- 工作流引擎
- 审批节点通过流程引擎中介传递任务
- 聊天系统
- 用户通过聊天室中介广播消息
注意事项与架构建议
⚠️ 注意事项
- 避免上帝对象
- 中介者不应包含过多业务逻辑(违反单一职责原则)
- 性能考量
- 高频通信场景需优化中介者转发效率
- 调试复杂性
- 交互链路间接化增加调试难度
✅ 架构建议
- 分层中介者
- 与观察者模式结合
- 用事件机制实现中介通知(
INotifyPropertyChanged
)
- 用事件机制实现中介通知(
- 接口隔离原则
- 为不同组件定义专用中介接口
public interface IDeviceMediator { void NotifyLightSensor(); void NotifyThermostat(); }
- DI容器集成
- 通过依赖注入管理中介者生命周期(.NET Core/Spring)
- 有限使用场景
- 仅在对象间存在复杂网状关系时使用
模式对比
模式 | 特点 | 适用场景 |
---|---|---|
中介者 | 集中控制多对象交互 | 复杂组件网络 |
观察者 | 一对多依赖关系 | 事件通知系统 |
外观 | 简化子系统访问接口 | 复杂子系统封装 |
黄金法则:当系统出现"蜘蛛网式耦合"(对象间引用混乱)时,优先考虑中介者模式重构。
- 备忘录
备忘录模式(Memento Pattern)详解
模式定义
备忘录模式是一种行为设计模式,它允许在不破坏对象封装性的前提下捕获并外部化对象的内部状态,以便后续可将该对象恢复到先前的状态。核心目标是提供状态的快照机制和回滚能力。
核心角色
Originator(发起人):
- 需要保存状态的对象(如文档编辑器、游戏角色)
- 提供创建备忘录(
CreateMemento()
)和恢复状态(RestoreMemento()
)的方法
Memento(备忘录):
- 存储Originator内部状态的不可变对象(通常设计为只读)
- 通过窄接口(如
GetState()
)暴露状态,保护封装性
Caretaker(管理者):
- 负责存储和管理备忘录(如历史记录栈)
- 无权修改或读取备忘录内容(符合迪米特法则)
常用场景示例
场景1:文档编辑器的撤销/重做(C#)
// Memento
public class DocumentMemento
{
public string Content { get; } // 只读属性保护状态
public DocumentMemento(string content) => Content = content;
}
// Originator
public class Document
{
public string Content { get; set; }
public DocumentMemento CreateMemento() => new(Content);
public void RestoreMemento(DocumentMemento memento)
=> Content = memento.Content;
}
// Caretaker
public class HistoryTracker
{
private readonly Stack<DocumentMemento> _history = new();
public void SaveState(Document doc)
=> _history.Push(doc.CreateMemento());
public void Undo(Document doc)
{
if (_history.Count > 0) doc.RestoreMemento(_history.Pop());
}
}
// 使用
var doc = new Document();
var history = new HistoryTracker();
doc.Content = "Version 1";
history.SaveState(doc); // 保存状态
doc.Content = "Version 2"; // 修改内容
history.Undo(doc); // 撤销到Version 1
场景2:游戏角色状态存档(Java)
// Memento
public class PlayerMemento {
private final int health;
private final String position;
public PlayerMemento(int health, String position) {
this.health = health;
this.position = position;
}
public int getHealth() { return health; } // 只提供Getter
public String getPosition() { return position; }
}
// Originator
public class Player {
private int health;
private String position;
public PlayerMemento save() {
return new PlayerMemento(health, position);
}
public void load(PlayerMemento memento) {
this.health = memento.getHealth();
this.position = memento.getPosition();
}
}
// Caretaker
public class SaveManager {
private Map<String, PlayerMemento> saves = new HashMap<>();
public void saveGame(String slot, Player player) {
saves.put(slot, player.save());
}
public void loadGame(String slot, Player player) {
if (saves.containsKey(slot)) player.load(saves.get(slot));
}
}
注意事项与架构建议
注意事项
内存消耗:
- 频繁保存大对象状态可能导致内存溢出(如高清图片编辑)
- 解决方案:增量备份、压缩存储、限制历史记录数量
封装性保护:
- 备忘录必须严格限制状态访问(C#用
internal
修饰,Java用包级私有) - 禁止Caretaker直接修改备忘录内容
- 备忘录必须严格限制状态访问(C#用
深拷贝问题:
- 对象包含引用类型时需深拷贝(如集合、嵌套对象)
- 示例:
DocumentMemento
中的Content
是字符串(不可变),无需深拷
生命周期管理:
- 长时间未使用的备忘录应及时清除(如LRU缓存策略)
架构建议
性能优化:
// 仅保存差异状态(增量备份) public class DiffMemento { public string ChangedText { get; } public int StartIndex { get; } // ... 其他差异元数据 }
支持多级撤销:
// Caretaker中使用双栈实现 Stack<Memento> undoStack = new Stack<>(); Stack<Memento> redoStack = new Stack<>();
持久化扩展:
- 备忘录可序列化为JSON/二进制存储到数据库
- 示例:
public string SerializeMemento(DocumentMemento m) => JsonConvert.SerializeObject(m);
安全控制:
- 对备忘录加密(如游戏存档防篡改)
- 添加版本号兼容旧版状态
结合其他模式:
- 命令模式:将备忘录绑定到命令对象实现事务操作
- 原型模式:通过克隆生成备忘录减少开销
经典应用案例
领域 | 应用实例 |
---|---|
文本编辑器 | Word/VS Code的撤销栈 |
图形设计软件 | Photoshop历史记录 |
游戏开发 | 角色状态存档/关卡进度保存 |
数据库系统 | 事务回滚(Transaction Rollback) |
版本控制系统 | Git的commit/checkout机制 |
黄金法则:当系统需要“时间机器”功能时优先考虑备忘录模式,但需警惕状态爆炸问题。建议通过
[Serializable]
(C#)或Serializable
接口(Java)支持跨进程状态恢复。
- 访问者
访问者模式详解
访问者模式(Visitor Pattern)是一种行为型设计模式,允许你将算法与操作的对象结构分离。它通过将操作逻辑移至独立的访问者类中,实现在不修改现有类结构的前提下添加新操作。
核心思想
- 双重分派(Double Dispatch):通过组合方法调用实现运行时多态
- 分离关注点:数据结构与数据操作解耦
- 开放封闭原则:新增操作只需添加新的访问者,无需修改元素类
模式结构
C# 实现示例
// 元素接口
public interface IElement
{
void Accept(IVisitor visitor);
}
// 具体元素A
public class ConcreteElementA : IElement
{
public void FeatureA() => Console.WriteLine("Feature A");
public void Accept(IVisitor visitor) => visitor.VisitElementA(this);
}
// 具体元素B
public class ConcreteElementB : IElement
{
public void FeatureB() => Console.WriteLine("Feature B");
public void Accept(IVisitor visitor) => visitor.VisitElementB(this);
}
// 访问者接口
public interface IVisitor
{
void VisitElementA(ConcreteElementA element);
void VisitElementB(ConcreteElementB element);
}
// 具体访问者
public class ConcreteVisitor : IVisitor
{
public void VisitElementA(ConcreteElementA element)
{
Console.WriteLine("Visitor processing ElementA");
element.FeatureA();
}
public void VisitElementB(ConcreteElementB element)
{
Console.WriteLine("Visitor processing ElementB");
element.FeatureB();
}
}
// 对象结构
public class ObjectStructure
{
private readonly List<IElement> _elements = new();
public void Attach(IElement element) => _elements.Add(element);
public void Detach(IElement element) => _elements.Remove(element);
public void Accept(IVisitor visitor)
{
foreach (var element in _elements)
{
element.Accept(visitor);
}
}
}
Java 实现示例
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素A
class ConcreteElementA implements Element {
public void featureA() {
System.out.println("Feature A");
}
@Override
public void accept(Visitor visitor) {
visitor.visitElementA(this);
}
}
// 具体元素B
class ConcreteElementB implements Element {
public void featureB() {
System.out.println("Feature B");
}
@Override
public void accept(Visitor visitor) {
visitor.visitElementB(this);
}
}
// 访问者接口
interface Visitor {
void visitElementA(ConcreteElementA element);
void visitElementB(ConcreteElementB element);
}
// 具体访问者
class ConcreteVisitor implements Visitor {
@Override
public void visitElementA(ConcreteElementA element) {
System.out.println("Visitor processing ElementA");
element.featureA();
}
@Override
public void visitElementB(ConcreteElementB element) {
System.out.println("Visitor processing ElementB");
element.featureB();
}
}
// 对象结构
class ObjectStructure {
private final List<Element> elements = new ArrayList<>();
public void attach(Element element) {
elements.add(element);
}
public void detach(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
常用应用场景
复杂对象结构操作
- 抽象语法树(AST)处理(编译器设计)
- 文档对象模型(DOM)处理
- UI组件树遍历
跨类层次的操作
- 报表生成(不同业务对象生成不同格式报表)
- 序列化/反序列化系统
- 权限检查系统
分离关注点
- 数据收集与统计(如电商订单各元素价格计算)
- 多格式导出系统(XML/JSON/CSV)
- 游戏实体行为处理(不同NPC类型的不同交互)
扩展框架功能
- IDE插件系统
- 工作流引擎扩展点
- 规则引擎执行器
关键注意事项
元素稳定性要求
- 适用场景:元素类结构稳定,但操作频繁变化
- 不适用场景:元素类需要频繁添加新类型
封装性破坏风险
- 访问者通常需要访问元素内部状态
- 解决方案:
- 提供受控访问接口
- 使用友元关系(C++)
- 包级私有访问(Java)
循环依赖问题
- 元素和访问者相互引用
- 解决方案:通过接口解耦,避免具体类依赖
遍历责任归属
- 明确对象结构负责遍历还是访问者负责
- 推荐:对象结构控制遍历顺序(如组合模式)
架构设计建议
访问者层次设计
与组合模式结合
public class CompositeElement : IElement { private readonly List<IElement> _children = new(); public void Add(IElement element) => _children.Add(element); public void Accept(IVisitor visitor) { visitor.VisitComposite(this); foreach (var child in _children) { child.Accept(visitor); } } }
访问者状态管理
- 有状态访问者:适合数据聚合场景(如价格计算)
- 无状态访问者:适合纯操作场景(如格式转换)
性能优化策略
- 访问者缓存:复用访问者实例
- 遍历优化:增量遍历大型结构
- 并行处理:线程安全访问者
访问控制机制
- 基于角色的访问控制:
public class RoleBasedVisitor implements Visitor { private final UserRole role; public RoleBasedVisitor(UserRole role) { this.role = role; } @Override public void visitElementA(ConcreteElementA element) { if(role.canAccess(element)) { // 执行操作 } } }
最佳实践总结
适用场景判断
- 当操作比元素类型更易变时优先选用
- 当需要跨不相关类层次执行操作时使用
设计权衡
- 优势:符合开闭原则,集中相关操作
- 代价:增加系统复杂度,元素接口扩展困难
替代方案
- 简单场景:考虑使用迭代器+策略模式组合
- 功能扩展:AOP(面向切面编程)
架构集成
- 框架设计:作为扩展点机制
- 领域驱动设计:分离领域模型与技术实现
访问者模式在复杂业务系统中能显著提升架构的扩展性和可维护性,但需谨慎评估其对系统复杂度的增加。在编译器设计、复杂报表系统等场景中表现尤为出色,是系统架构师解决操作扩展问题的有力工具。
19. 模板方法
模板方法模式详解(系统架构师视角)
模式定义
模板方法模式是一种行为设计模式,它在抽象类中定义算法的骨架,将某些步骤延迟到子类实现。该模式通过固定算法结构确保核心流程不变,同时允许子类重写特定步骤的实现。
核心结构
// Java示例
abstract class AbstractClass {
// 模板方法(final防止子类覆盖算法结构)
public final void templateMethod() {
step1();
step2(); // 抽象步骤
step3();
if (hook()) { // 钩子方法
step4();
}
}
// 固定步骤(可提供默认实现)
private void step1() {
System.out.println("固定步骤1");
}
// 抽象步骤(子类必须实现)
protected abstract void step2();
// 可选步骤(子类可覆盖)
protected void step3() {
System.out.println("默认步骤3");
}
// 钩子方法(控制流程扩展点)
protected boolean hook() {
return true;
}
}
// C#示例
abstract class AbstractClass {
public void TemplateMethod() {
Step1();
Step2(); // 抽象步骤
Step3();
if (Hook()) { // 钩子方法
Step4();
}
}
private void Step1() => Console.WriteLine("固定步骤1");
protected abstract void Step2();
protected virtual void Step3() => Console.WriteLine("默认步骤3");
protected virtual bool Hook() => true;
}
典型应用场景
框架扩展点设计
- 场景:开发框架时固定核心流程(如Spring的事务管理、Servlet生命周期)
- 示例:
// Java Servlet public abstract class HttpServlet { protected void service(HttpServletRequest req, HttpServletResponse resp) { // 固定流程 if (req.getMethod().equals("GET")) doGet(...); else if (req.getMethod().equals("POST")) doPost(...); } protected abstract void doGet(...); // 子类实现 }
跨平台业务逻辑
- 场景:多平台实现相同流程但细节不同(如支付流程:验证→执行→日志)
- 示例:
// C#支付处理 abstract class PaymentProcessor { public void Process() { Validate(); ExecutePayment(); // 平台相关实现 LogResult(); } protected abstract void ExecutePayment(); }
算法扩展
- 场景:算法结构固定但部分步骤可变(如数据解析:读数据→解析→输出)
- 示例:Java集合框架的
AbstractList
定义骨架方法,子类实现get()
等
生命周期管理
- 场景:对象创建/初始化的固定流程(如游戏角色生成:加载资源→初始化状态→注册事件)
abstract class GameCharacter { public final void init() { loadModel(); initAI(); // 子类自定义AI逻辑 registerEvents(); } protected abstract void initAI(); }
- 场景:对象创建/初始化的固定流程(如游戏角色生成:加载资源→初始化状态→注册事件)
架构注意事项
流程控制
- ✅ 保护模板方法:用
final
(Java)/sealed
(C#)修饰模板方法防止子类破坏结构 - ⚠️ 避免过度拆分:抽象步骤不宜过多(通常3-5个),否则增加子类实现负担
- ✅ 保护模板方法:用
扩展性设计
- 钩子方法:提供带默认实现的虚方法(如
hook()
),允许子类影响主流程 - 好莱坞原则:“Don’t call us, we’ll call you” – 子类不应直接调用父类
- 钩子方法:提供带默认实现的虚方法(如
继承风险
- ❗ 里氏替换原则:子类扩展时不得改变父类既定行为
- ⚠️ 组合替代:若存在多重变化维度,考虑结合策略模式
架构最佳实践
层次化抽象
文档规范
- 明确标注抽象方法的契约(前置/后置条件)
- 示例:Java注释
/** * 实现支付执行逻辑(必须保证幂等性) * @throws PaymentException 超过重试次数时抛出 */ protected abstract void executePayment();
防御式编程
- 在模板方法中添加关键检查:
public void TemplateMethod() { if (!IsValidState()) throw new InvalidOperationException(); // ...继续流程 }
- 在模板方法中添加关键检查:
与其它模式协作
- 工厂方法模式:在模板方法中调用工厂方法创建对象
- 策略模式:将可变步骤委托给策略接口,避免继承膨胀
反模式警示
流程碎片化
❌ 错误:将算法拆分成过多细碎步骤
✅ 改进:合并相关步骤,保持高内聚子类耦合父类
❌ 错误:子类方法直接调用父类具体方法
✅ 改进:通过抽象方法隔离依赖过度使用继承
graph LR A[模板方法模式] --> B{是否需要支持运行时变更行为?} B -->|是| C[改用策略模式] B -->|否| D[继续使用模板方法]
性能考量
- 虚方法调用成本:在高性能场景(如金融交易),避免深层继承树
- 解决方案:C#使用
sealed
修饰叶子子类,Java启用JIT内联优化
架构师决策点:当系统需要 强制统一流程 且存在 可扩展步骤 时优先选用模板方法模式,在框架设计和跨平台实现中尤为有效。同时需警惕"继承地狱",必要时结合组合模式优化结构。
- 解释器
解释器模式详解
模式定义
解释器模式(Interpreter Pattern) 是一种行为设计模式,用于定义语言的文法规则,并构建解释器来解释语言中的表达式。它将语法解析分解为多个小类,通过组合这些类实现语法解释功能。
核心组件
组件 | 说明 |
---|---|
AbstractExpression | 抽象表达式接口,声明解释操作(如 Interpret() ) |
TerminalExpression | 终结符表达式,实现与文法中终结符相关的解释(如变量、常量) |
NonterminalExpression | 非终结符表达式,通过组合子表达式解释复杂语法(如运算符、逻辑规则) |
Context | 全局上下文信息(如变量存储、函数表) |
Client | 构建语法树并触发解释操作 |
C# / Java 代码实现
场景示例:SQL WHERE 条件解析
假设需要解析类似 "Age > 25 AND Country = 'USA'"
的条件表达式。
C# 实现
// 抽象表达式
public interface IExpression {
bool Interpret(Dictionary<string, object> context);
}
// 终结符表达式:变量
public class VariableExpression : IExpression {
private string _key;
public VariableExpression(string key) => _key = key;
public bool Interpret(Dictionary<string, object> context)
=> context.ContainsKey(_key);
}
// 终结符表达式:常量
public class ConstantExpression : IExpression {
private object _value;
public ConstantExpression(object value) => _value = value;
public bool Interpret(Dictionary<string, object> context)
=> _value != null;
}
// 非终结符表达式:等于操作
public class EqualsExpression : IExpression {
private IExpression _left, _right;
public EqualsExpression(IExpression left, IExpression right)
=> (_left, _right) = (left, right);
public bool Interpret(Dictionary<string, object> context) {
dynamic leftVal = context[(_left as VariableExpression)?._key ?? ""];
dynamic rightVal = (_right as ConstantExpression)?._value;
return leftVal == rightVal;
}
}
// 非终结符表达式:AND 操作
public class AndExpression : IExpression {
private IExpression _expr1, _expr2;
public AndExpression(IExpression expr1, IExpression expr2)
=> (_expr1, _expr2) = (expr1, expr2);
public bool Interpret(Dictionary<string, object> context)
=> _expr1.Interpret(context) && _expr2.Interpret(context);
}
// 客户端调用
var context = new Dictionary<string, object> {
{ "Age", 30 },
{ "Country", "USA" }
};
var expr = new AndExpression(
new GreaterThanExpression(
new VariableExpression("Age"),
new ConstantExpression(25)
),
new EqualsExpression(
new VariableExpression("Country"),
new ConstantExpression("USA")
)
);
Console.WriteLine(expr.Interpret(context)); // 输出: True
Java 实现
// 抽象表达式
interface Expression {
boolean interpret(Map<String, Object> context);
}
// 终结符:变量
class VariableExpression implements Expression {
private String key;
public VariableExpression(String key) { this.key = key; }
public boolean interpret(Map<String, Object> context) {
return context.containsKey(key);
}
}
// 终结符:常量
class ConstantExpression implements Expression {
private Object value;
public ConstantExpression(Object value) { this.value = value; }
public boolean interpret(Map<String, Object> context) {
return value != null;
}
}
// 非终结符:等于操作
class EqualsExpression implements Expression {
private Expression left, right;
public EqualsExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public boolean interpret(Map<String, Object> context) {
Object leftVal = context.get(((VariableExpression) left).key);
Object rightVal = ((ConstantExpression) right).value;
return leftVal.equals(rightVal);
}
}
// 客户端
Map<String, Object> context = new HashMap<>();
context.put("Age", 30);
context.put("Country", "USA");
Expression expr = new AndExpression(
new GreaterThanExpression(
new VariableExpression("Age"),
new ConstantExpression(25)
),
new EqualsExpression(
new VariableExpression("Country"),
new ConstantExpression("USA"))
);
System.out.println(expr.interpret(context)); // 输出: true
常用应用场景
- 领域特定语言(DSL)
- 规则引擎(如Drools)
- 业务规则配置(如折扣规则、风控规则)
- 语法解析
- SQL WHERE 条件解析
- 数学公式计算器
- 配置文件解析
- 自定义格式的配置规则
- 游戏脚本系统
- 正则表达式引擎
- 模式匹配解释器
注意事项与架构建议
⚠️ 注意事项
- 性能问题
- 深层嵌套语法树会降低性能(考虑缓存解释结果)
- 避免在性能敏感场景使用(如高频交易)
- 文法复杂度
- 仅适合简单文法(BNF范式可描述)
- 复杂文法需用解析器生成器(如ANTLR)
- 维护成本
- 每增加一条规则需新增类
- 过度使用会导致类膨胀
🏗️ 架构建议
- 结合组合模式
- 用树形结构组织表达式(如
CompositeExpression
)
- 用树形结构组织表达式(如
- 分离解析与执行
- 使用单独解析器构建语法树
- 解释器仅负责执行
- 上下文优化
- 线程安全的上下文设计
- 预编译常用表达式(如预计算常量)
- 扩展性设计
// 通过装饰器增加新功能 public class CachedExpression : IExpression { private IExpression _expr; private bool? _lastResult; public bool Interpret(Dictionary<string, object> context) { return _lastResult ??= _expr.Interpret(context); } }
- 替代方案
- 复杂场景用访问者模式遍历语法树
- 考虑现成工具(如Roslyn、Eclipse JDT)
总结
适用场景:
- 需要灵活扩展语法规则
- 文法简单且执行频率不高
- 避免重复解析相同逻辑(如模板引擎)
规避场景:
- 高性能需求系统
- 文法规则频繁变化
- 复杂语法(如完整编程语言)
通过合理设计,解释器模式能高效解决特定领域语言解析问题,但需警惕其潜在复杂性和性能瓶颈。在实际架构中,优先评估是否可用现成解析库替代自定义实现。
21. 享元
享元模式(Flyweight Pattern)详解
模式定义
享元模式是一种结构型设计模式,通过共享大量细粒度对象来减少内存占用和提高性能。核心思想是将对象状态分为:
- 内部状态(Intrinsic):可共享的、不变的部分(如字符编码)
- 外部状态(Extrinsic):不可共享的、变化的部分(如位置坐标)
模式结构
C# 实现示例
// 抽象享元
public interface ITextCharacter {
void Display(int position);
}
// 具体享元(包含内部状态)
public class Character : ITextCharacter {
private readonly char _symbol; // 内部状态
public Character(char symbol) => _symbol = symbol;
public void Display(int position) =>
Console.WriteLine($"Symbol: {_symbol}, Position: {position}");
}
// 享元工厂
public class CharacterFactory {
private readonly Dictionary<char, ITextCharacter> _characters = new();
public ITextCharacter GetCharacter(char key) {
if (!_characters.TryGetValue(key, out var character)) {
character = new Character(key);
_characters.Add(key, character);
}
return character;
}
}
// 客户端
var factory = new CharacterFactory();
var charA = factory.GetCharacter('A');
charA.Display(10); // 外部状态:位置
Java 实现示例
// 抽象享元
interface TextCharacter {
void display(int position);
}
// 具体享元
class Character implements TextCharacter {
private final char symbol; // 内部状态
public Character(char symbol) { this.symbol = symbol; }
@Override
public void display(int position) {
System.out.println("Symbol: " + symbol + ", Position: " + position);
}
}
// 享元工厂
class CharacterFactory {
private final Map<Character, TextCharacter> pool = new HashMap<>();
public TextCharacter getCharacter(char key) {
return pool.computeIfAbsent(key, Character::new);
}
}
// 客户端
CharacterFactory factory = new CharacterFactory();
TextCharacter charA = factory.getCharacter('A');
charA.display(10);
常用场景
文本编辑器
- 共享字符对象(内部状态:字体、大小)
- 外部状态:位置、颜色
游戏开发
- 共享粒子/树木/武器对象(内部状态:纹理、模型)
- 外部状态:位置、旋转角度
图形处理
- 共享图形对象(内部状态:形状类型)
- 外部状态:坐标、缩放比例
数据库连接池
- 共享连接对象(内部状态:配置)
- 外部状态:会话状态
UI组件库
- 共享按钮/图标(内部状态:样式)
- 外部状态:位置、点击事件
注意事项
线程安全问题
- 共享对象需设计为不可变(Immutable)
- 使用线程安全容器(如
ConcurrentDictionary
)
外部状态管理
- 客户端负责维护外部状态
- 避免外部状态污染内部状态
性能权衡
- 对象共享会引入查找开销(工厂模式)
- 适用于对象创建成本高且数量大的场景
内存泄漏风险
- 长期持有未使用的享元对象
- 解决方案:弱引用(
WeakReference
)或定期清理
架构建议
结合工厂模式
- 强制通过工厂获取对象,确保共享机制
与组合模式结合
- 处理树形结构时,共享叶子节点
// 组合模式示例 public class TreeNode : IComponent { private readonly ITextCharacter _character; private readonly List<IComponent> _children = new(); public void Add(IComponent comp) => _children.Add(comp); public void Render(int x) { _character.Display(x); foreach (var child in _children) child.Render(x + 10); } }
外部状态存储优化
- 使用轻量结构存储外部状态(如元组、结构体)
- 避免外部状态包含大对象
性能监控
- 跟踪享元对象命中率(Hit Rate)
- 当命中率<70%时,考虑是否滥用模式
区分共享边界
- 明确划分线程级共享/进程级共享
- 分布式场景使用享元代理(Flyweight Proxy)
经典案例:游戏地图渲染
// 地形类型(内部状态)
enum TerrainType { FOREST, MOUNTAIN, WATER }
// 享元对象
class Terrain {
private final TerrainType type;
private final String texture; // 大纹理数据(共享)
public Terrain(TerrainType type, String texture) { ... }
public void render(int x, int y) {
// 使用外部状态(x,y)渲染
}
}
// 客户端使用
Terrain forest = factory.getTerrain(TerrainType.FOREST);
for (int i=0; i<1000; i++) {
forest.render(randomX(), randomY()); // 外部状态
}
节省效果:1,000,000个地形成分 → 仅需3个享元对象
反模式警示
强行共享
- 对象差异过大时,维护成本高于内存节省
忽略外部状态成本
- 频繁计算外部状态导致CPU开销剧增
过度设计
- 小规模对象池(<1000实例)无需享元模式
性能优化技巧
懒加载 + 预加载结合
public class OptimizedFactory { private ConcurrentDictionary<char, Lazy<ITextCharacter>> _pool = new ConcurrentDictionary<>(); public ITextCharacter GetChar(char key) { return _pool.GetOrAdd(key, k => new Lazy<ITextCharacter>(() => new Character(k)) ).Value; } }
使用值对象(Value Object)
- C# 中
readonly struct
/ Java 中record
类 - 确保内部状态不可变
- C# 中
分级存储策略
总结
适用场景:存在大量重复对象、对象状态可分拆、内存压力显著
规避场景:对象差异性大、外部状态维护成本高、系统规模小
最佳实践:
- 严格区分内部/外部状态
- 工厂类添加内存监控钩子
- 与对象池模式结合处理非共享对象
- 在游戏引擎、文档处理器等重资源场景优先采用
- 代理
代理模式详解
核心概念
代理模式是一种结构型设计模式,通过创建代理对象控制对目标对象的访问。代理在客户端和目标对象之间充当中介,提供额外的逻辑层(如访问控制、延迟加载、日志记录等),遵循开闭原则。
三种角色
- Subject(抽象主题)
定义目标对象和代理的公共接口 - RealSubject(真实主题)
实际执行业务逻辑的目标对象 - Proxy(代理)
持有真实主题的引用,控制访问并添加增强功能
应用场景与代码示例
1. 虚拟代理(延迟加载)
场景:初始化开销大的对象(如图片/文件加载)
// C# 示例
public interface IImage { void Display(); }
public class HighResImage : IImage {
public HighResImage(string path) => LoadImage(path); // 高开销操作
public void Display() => Console.WriteLine("显示高清图片");
}
public class ImageProxy : IImage {
private string _path;
private HighResImage _realImage;
public void Display() {
_realImage ??= new HighResImage(_path); // 延迟初始化
_realImage.Display();
}
}
// Java 示例
interface Image { void display(); }
class RealImage implements Image {
public RealImage(String path) { loadFromDisk(path); }
public void display() { System.out.println("显示图片"); }
}
class ProxyImage implements Image {
private String path;
private RealImage realImage;
@Override
public void display() {
if (realImage == null) realImage = new RealImage(path);
realImage.display();
}
}
2. 保护代理(访问控制)
场景:敏感操作权限验证
// C# 示例
public interface IDatabase { void Query(string sql); }
public class RealDatabase : IDatabase {
public void Query(string sql) => Console.WriteLine($"执行: {sql}");
}
public class AuthProxy : IDatabase {
private RealDatabase _db = new();
private string _userRole;
public void Query(string sql) {
if (_userRole == "Admin") _db.Query(sql);
else throw new UnauthorizedAccessException("权限不足");
}
}
3. 远程代理(网络通信)
场景:分布式系统跨进程调用(Java RMI 示例)
// Java RMI 接口
public interface RemoteService extends Remote {
String process() throws RemoteException;
}
// 客户端调用
Registry registry = LocateRegistry.getRegistry("host", 1099);
RemoteService service = (RemoteService) registry.lookup("ServiceName");
service.process(); // 代理处理网络通信
4. 日志记录代理
场景:审计关键操作
// C# 日志代理
public class LoggingProxy : IDatabase {
private IDatabase _target;
public void Query(string sql) {
Log($"执行SQL: {sql} 时间: {DateTime.Now}");
_target.Query(sql);
}
}
架构建议与注意事项
最佳实践
接口一致性原则
确保代理与真实主题实现相同接口
动态代理优先
- Java: 使用
java.lang.reflect.Proxy
- C#: 使用
DispatchProxy
或第三方库(如 Castle DynamicProxy)
// Java 动态代理示例 InvocationHandler handler = (proxy, method, args) -> { System.out.println("前置处理"); return method.invoke(target, args); }; Subject proxy = (Subject) Proxy.newProxyInstance( loader, new Class[]{Subject.class}, handler);
- Java: 使用
代理链组合
通过责任链模式组合多个代理:客户端 -> [日志代理] -> [缓存代理] -> [真实对象]
注意事项
性能影响
- 避免多层嵌套代理(特别是远程代理)
- 高频调用场景慎用
初始化陷阱
虚拟代理需注意线程安全问题:// C# 线程安全延迟初始化 private Lazy<RealSubject> _subject = new Lazy<RealSubject>( () => new RealSubject(), LazyThreadSafetyMode.ExecutionAndPublication);
循环依赖
代理与真实对象避免相互引用异常处理
远程代理需处理网络异常:try { remoteService.process(); } catch (RemoteException e) { logger.error("网络调用失败", e); }
典型应用框架
框架 | 代理类型 | 作用 |
---|---|---|
Spring AOP | JDK动态代理 | 切面编程 |
Entity Framework | 延迟加载代理 | 按需加载关联数据 |
WCF | 服务代理 | 远程服务调用 |
Hibernate | 字节码增强 | 属性延迟加载 |
总结
代理模式适用场景:
- 需要控制对象访问(权限/生命周期)
- 添加与核心逻辑无关的横切关注点(日志/监控)
- 优化资源使用(延迟加载/缓存)
- 简化复杂系统访问(远程调用)
替代方案:
- 装饰器模式:侧重功能增强而非访问控制
- 外观模式:封装子系统接口,不控制单个对象
模式选择速查表
问题类型 | 推荐模式 |
---|---|
全局访问点 | 单例模式 |
灵活创建对象 | 工厂方法/抽象工厂 |
分步构建复杂对象 | 建造者模式 |
接口不兼容 | 适配器模式 |
多维度扩展 | 桥接模式 |
树形结构处理 | 组合模式 |
动态添加功能 | 装饰器模式 |
简化复杂系统 | 外观模式 |
状态驱动行为 | 状态模式 |
算法自由切换 | 策略模式 |
事件通知机制 | 观察者模式 |
操作封装 | 命令模式 |
黄金法则:
- 优先组合而非继承
- 面向接口编程
- 高内聚低耦合
- 对修改关闭,对扩展开放(开闭原则)
实际案例:电商系统典型模式组合
- 支付模块:策略模式+工厂方法
- 订单管理:状态模式+观察者模式
- 商品展示:装饰器模式(价格修饰)
- 权限控制:代理模式
- 日志系统:责任链模式+适配器模式