23种设计模式解析--行为型
行为型模式(协作的艺术)
- 观察者模式
观察者模式详解
模式定义
观察者模式(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; }@Overridepublic 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)是一种行为设计模式,它定义一系列算法,封装每个算法,并使它们可以相互替换。策略模式让算法的变化独立于使用它的客户端。
核心结构
classDiagramclass Context {-IStrategy strategy+SetStrategy(IStrategy)+ExecuteStrategy()}interface IStrategy {+Execute()}class ConcreteStrategyA {+Execute()}class ConcreteStrategyB {+Execute()}Context o--> IStrategyIStrategy <|.. ConcreteStrategyAIStrategy <|.. 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 {@Overridepublic void processPayment(BigDecimal amount) {System.out.println("WeChat Pay: " + amount);}
}class Alipay implements PaymentStrategy {@Overridepublic 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;@Overridepublic 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; }@Overridepublic void execute() { light.turnOn(); }@Overridepublic 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 {@Overridepublic 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"};@Overridepublic Iterator<String> createIterator() {return new ConcreteIterator();}private class ConcreteIterator implements Iterator<String> {private int index = 0;@Overridepublic boolean hasNext() {return index < items.length;}@Overridepublic 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");}@Overridepublic 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; } // 只提供Getterpublic 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");}@Overridepublic void accept(Visitor visitor) {visitor.visitElementA(this);}
}// 具体元素B
class ConcreteElementB implements Element {public void featureB() {System.out.println("Feature B");}@Overridepublic void accept(Visitor visitor) {visitor.visitElementB(this);}
}// 访问者接口
interface Visitor {void visitElementA(ConcreteElementA element);void visitElementB(ConcreteElementB element);
}// 具体访问者
class ConcreteVisitor implements Visitor {@Overridepublic void visitElementA(ConcreteElementA element) {System.out.println("Visitor processing ElementA");element.featureA();}@Overridepublic 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;}@Overridepublic 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 LRA[模板方法模式] --> 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; }@Overridepublic 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;@Overridepublic 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 | 字节码增强 | 属性延迟加载 |
总结
代理模式适用场景:
- 需要控制对象访问(权限/生命周期)
- 添加与核心逻辑无关的横切关注点(日志/监控)
- 优化资源使用(延迟加载/缓存)
- 简化复杂系统访问(远程调用)
替代方案:
- 装饰器模式:侧重功能增强而非访问控制
- 外观模式:封装子系统接口,不控制单个对象
模式选择速查表
问题类型 | 推荐模式 |
---|---|
全局访问点 | 单例模式 |
灵活创建对象 | 工厂方法/抽象工厂 |
分步构建复杂对象 | 建造者模式 |
接口不兼容 | 适配器模式 |
多维度扩展 | 桥接模式 |
树形结构处理 | 组合模式 |
动态添加功能 | 装饰器模式 |
简化复杂系统 | 外观模式 |
状态驱动行为 | 状态模式 |
算法自由切换 | 策略模式 |
事件通知机制 | 观察者模式 |
操作封装 | 命令模式 |
黄金法则:
- 优先组合而非继承
- 面向接口编程
- 高内聚低耦合
- 对修改关闭,对扩展开放(开闭原则)
实际案例:电商系统典型模式组合
- 支付模块:策略模式+工厂方法
- 订单管理:状态模式+观察者模式
- 商品展示:装饰器模式(价格修饰)
- 权限控制:代理模式
- 日志系统:责任链模式+适配器模式