行为型设计模式详解
行为型设计模式关注对象之间的通信机制,解决对象间责任分配和算法封装的问题。这类模式描述对象如何协作完成单个对象无法完成的任务,以及如何分配职责使对象间的耦合度最小。
行为型模式 = 对象通信 + 责任分配 + 算法封装 + 松耦合设计
行为型模式概览
行为型设计模式主要包括以下11种模式,各自针对不同的行为交互场景:
| 模式名称 | 核心意图 | 关键特征 | 典型应用场景 |
|---|---|---|---|
| 观察者模式 | 定义对象间的一种一对多依赖关系 | 发布-订阅机制、松耦合通知 | 事件处理系统、UI更新、消息推送 |
| 策略模式 | 定义一系列算法,使其可以互相替换 | 算法封装、运行时切换 | 支付方式选择、排序算法切换 |
| 命令模式 | 将请求封装为对象,支持撤销等操作 | 行为参数化、请求队列 | 事务处理、命令撤销、GUI操作 |
| 状态模式 | 允许对象在内部状态改变时改变行为 | 状态驱动行为、状态转换 | 工作流系统、游戏状态管理 |
| 责任链模式 | 多个处理对象组成链,依次处理请求 | 动态链构建、请求传递 | 审批流程、异常处理、过滤器 |
| 解释器模式 | 定义语言语法表示,并解释其句子 | 语法树构建、规则解析 | 表达式计算、SQL解析、正则表达式 |
| 迭代器模式 | 提供顺序访问集合元素的方法 | 遍历与实现分离、内部迭代器 | 集合遍历、数据流处理 |
| 中介者模式 | 封装一组对象如何交互 | 中心化通信、多对多解耦 | 聊天室、航空管制、GUI组件交互 |
| 备忘录模式 | 在不违反封装的前提下保存对象状态 | 状态捕获、历史回溯 | 编辑器撤销功能、游戏存档 |
| 模板方法模式 | 定义算法骨架,允许子类重定义特定步骤 | 算法复用、钩子方法 | 构建流程、数据处理流程 |
| 访问者模式 | 将算法与对象结构分离 | 双分派、结构与操作分离 | 复杂对象操作、报表生成 |
观察者模式 (Observer Pattern)
观察者模式是一种行为设计模式,它定义了对象间的一种一对多依赖关系,使得当一个对象状态改变时,所有依赖于它的对象都会得到通知并自动更新。
- 适用场景
- 优点
- 缺点
- 当一个对象的状态变化需要通知其他多个对象时
- 当应用中某些对象需要观察其他对象时,但仅在有限时间内或特定情况下
- 当对象间存在一对多依赖,而且依赖关系不是永久性的
- 需要建立一套触发机制的场景
- 遵循开闭原则,可以在不修改主题的情况下添加观察者
- 建立了对象之间的松耦合关系
- 支持广播通信,一个主题可以通知多个观察者
- 可以在运行时建立和解除对象间的关联
- 观察者被通知的顺序是随机的,难以控制
- 如果观察者之间存在循环依赖,可能导致系统崩溃
- 观察者模式实现不当容易引起内存泄漏
- 如果观察者过多或处理逻辑过重,可能导致性能问题
观察者模式结构
观察者模式主要由以下几个角色组成:
- 主题(Subject):定义添加、删除和通知观察者的接口
- 具体主题(ConcreteSubject):实现主题接口,维护观察者列表,状态变更时通知观察者
- 观察者(Observer):定义接收通知时的更新接口
- 具体观察者(ConcreteObserver):实现观察者接口,维护与主题的一致性,存储感兴趣的状态
观察者模式实现
下面是一个典型的观察者模式实现:
1// 观察者接口2public interface Observer {3 void update(String message);4}56// 主题接口7public interface Subject {8 void attach(Observer observer);9 void detach(Observer observer);10 void notifyObservers();11}1213// 具体主题14public class NewsAgency implements Subject {15 private List<Observer> observers = new ArrayList<>();16 private String news;17 18 @Override19 public void attach(Observer observer) {20 observers.add(observer);21 }22 23 @Override24 public void detach(Observer observer) {25 observers.remove(observer);26 }27 28 @Override29 public void notifyObservers() {30 for (Observer observer : observers) {31 observer.update(news);32 }33 }34 35 public void setNews(String news) {36 this.news = news;37 notifyObservers();38 }39}4041// 具体观察者42public class NewsChannel implements Observer {43 private String name;44 45 public NewsChannel(String name) {46 this.name = name;47 }48 49 @Override50 public void update(String news) {51 System.out.println(name + " 收到新闻: " + news);52 }53}5455// 使用示例56public class ObserverDemo {57 public static void main(String[] args) {58 NewsAgency agency = new NewsAgency();59 60 Observer tv1 = new NewsChannel("CCTV");61 Observer tv2 = new NewsChannel("BBC");62 Observer tv3 = new NewsChannel("CNN");63 64 agency.attach(tv1);65 agency.attach(tv2);66 agency.attach(tv3);67 68 agency.setNews("重大新闻:观察者模式发布!");69 70 agency.detach(tv2); // BBC退订71 72 agency.setNews("后续报道:观察者模式运行良好!");73 }74}观察者模式变体
- 推模型 (Push)
- 拉模型 (Pull)
- 事件驱动模型
推模型是观察者模式的标准实现,主题将所有状态变化推送给观察者:
1// 推模型 - 主题将所有数据推送给观察者2public interface Observer {3 void update(String message, LocalDateTime timestamp, String category);4}56public class ConcreteObserver implements Observer {7 @Override8 public void update(String message, LocalDateTime timestamp, String category) {9 System.out.println("收到消息: " + message);10 System.out.println("时间: " + timestamp);11 System.out.println("类别: " + category);12 }13}拉模型中主题仅通知观察者有变化,观察者按需获取数据:
1// 拉模型 - 观察者主动从主题获取需要的数据2public interface Observer {3 void update(Subject subject);4}56public interface Subject {7 String getMessage();8 LocalDateTime getTimestamp();9 String getCategory();10 // 其他获取方法...11}1213public class ConcreteObserver implements Observer {14 @Override15 public void update(Subject subject) {16 // 只获取需要的数据17 String message = subject.getMessage();18 System.out.println("收到消息: " + message);19 20 // 按需获取其他数据21 if (needTimestamp()) {22 LocalDateTime timestamp = subject.getTimestamp();23 System.out.println("时间: " + timestamp);24 }25 }26 27 private boolean needTimestamp() {28 return true; // 根据实际需要决定29 }30}事件驱动模型使用专门的事件对象来传递信息:
1// 事件类2public class NewsEvent {3 private final String message;4 private final LocalDateTime timestamp;5 private final String category;6 7 public NewsEvent(String message, String category) {8 this.message = message;9 this.timestamp = LocalDateTime.now();10 this.category = category;11 }12 13 // getter方法...14}1516public interface EventListener {17 void onEvent(NewsEvent event);18}1920public class EventSource {21 private List<EventListener> listeners = new ArrayList<>();22 23 public void addListener(EventListener listener) {24 listeners.add(listener);25 }26 27 public void removeListener(EventListener listener) {28 listeners.remove(listener);29 }30 31 public void fireEvent(NewsEvent event) {32 for (EventListener listener : listeners) {33 listener.onEvent(event);34 }35 }36}观察者模式应用实例
- 事件处理系统
- 消息订阅系统
1// 事件处理系统中的观察者模式2public interface EventListener {3 void handleEvent(Event event);4}56public class UIButton {7 private List<EventListener> clickListeners = new ArrayList<>();8 9 public void addClickListener(EventListener listener) {10 clickListeners.add(listener);11 }12 13 public void removeClickListener(EventListener listener) {14 clickListeners.remove(listener);15 }16 17 public void click() {18 Event event = new Event("click", this);19 for (EventListener listener : clickListeners) {20 listener.handleEvent(event);21 }22 }23}2425// 具体监听器26public class SaveButtonListener implements EventListener {27 @Override28 public void handleEvent(Event event) {29 if (event.getType().equals("click")) {30 System.out.println("保存文档...");31 saveDocument();32 }33 }34 35 private void saveDocument() {36 // 保存文档的逻辑37 }38}3940// 使用示例41public static void main(String[] args) {42 UIButton saveButton = new UIButton();43 saveButton.addClickListener(new SaveButtonListener());44 45 // 用户点击按钮46 saveButton.click();47}1// 基于主题的消息订阅系统2public class MessageBroker {3 private Map<String, List<Subscriber>> subscribers = new HashMap<>();4 5 public void subscribe(String topic, Subscriber subscriber) {6 subscribers.computeIfAbsent(topic, k -> new ArrayList<>()).add(subscriber);7 }8 9 public void unsubscribe(String topic, Subscriber subscriber) {10 if (subscribers.containsKey(topic)) {11 subscribers.get(topic).remove(subscriber);12 }13 }14 15 public void publish(String topic, String message) {16 if (subscribers.containsKey(topic)) {17 for (Subscriber subscriber : subscribers.get(topic)) {18 subscriber.receive(topic, message);19 }20 }21 }22}2324public interface Subscriber {25 void receive(String topic, String message);26}2728public class EmailNotifier implements Subscriber {29 private String email;30 31 public EmailNotifier(String email) {32 this.email = email;33 }34 35 @Override36 public void receive(String topic, String message) {37 System.out.println("向 " + email + " 发送关于 " + topic + " 的邮件通知: " + message);38 }39}4041public class SMSNotifier implements Subscriber {42 private String phoneNumber;43 44 public SMSNotifier(String phoneNumber) {45 this.phoneNumber = phoneNumber;46 }47 48 @Override49 public void receive(String topic, String message) {50 System.out.println("向 " + phoneNumber + " 发送关于 " + topic + " 的短信通知: " + message);51 }52}5354// 使用示例55public static void main(String[] args) {56 MessageBroker broker = new MessageBroker();57 58 Subscriber emailSub = new EmailNotifier("user@example.com");59 Subscriber smsSub = new SMSNotifier("13800138000");60 61 broker.subscribe("sports", emailSub);62 broker.subscribe("tech", emailSub);63 broker.subscribe("tech", smsSub);64 65 broker.publish("tech", "新型AI技术发布");66 broker.publish("sports", "世界杯最新赛况");67}观察者模式与其他模式比较
| 模式 | 区别 | 组合使用场景 |
|---|---|---|
| 观察者模式 vs 中介者模式 | 观察者模式中观察者知道主题的存在,而中介者模式中组件通过中介者间接通信 | 中介者可以作为观察者实现,或者使用观察者模式监听中介者状态 |
| 观察者模式 vs 发布-订阅模式 | 发布-订阅通常有一个事件通道作为中间层,发布者和订阅者互不了解 | 可以使用观察者模式实现发布-订阅模式的核心机制 |
| 观察者模式 vs 命令模式 | 命令模式封装请求为对象,观察者模式处理对象间的事件通知 | 命令对象可以作为观察者,在事件发生时执行特定命令 |
观察者模式实现要点
-
线程安全考虑:在多线程环境中,观察者列表和通知机制需要考虑同步问题
java1// 线程安全的观察者列表2private final List<Observer> observers =3 Collections.synchronizedList(new ArrayList<>());45// 或使用并发集合6private final CopyOnWriteArrayList<Observer> observers =7 new CopyOnWriteArrayList<>(); -
防止内存泄漏:弱引用可以防止观察者持有导致的内存泄漏
java1// 使用弱引用存储观察者2private final List<WeakReference<Observer>> observers = new ArrayList<>();34public void notifyObservers() {5 Iterator<WeakReference<Observer>> iterator = observers.iterator();6 while (iterator.hasNext()) {7 Observer observer = iterator.next().get();8 if (observer != null) {9 observer.update(this.state);10 } else {11 // 移除已被垃圾收集的观察者引用12 iterator.remove();13 }14 }15} -
异常处理:观察者处理过程中的异常不应影响其他观察者
java1public void notifyObservers() {2 for (Observer observer : observers) {3 try {4 observer.update(this.state);5 } catch (Exception e) {6 // 记录异常但继续通知其他观察者7 logger.error("通知观察者时发生错误", e);8 }9 }10}
- 明确定义观察者和主题的接口,保持关注点分离
- 考虑使用JDK内置的Observable类和Observer接口
- 对于复杂系统,考虑使用事件总线或消息中间件实现
- 避免在观察者的update方法中执行耗时操作
- 当观察者数量庞大时,考虑使用批处理或异步通知
策略模式 (Strategy Pattern)
策略模式是一种行为设计模式,它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。
- 适用场景
- 优点
- 缺点
- 当需要使用不同变体的算法时
- 当需要隐藏算法实现细节时
- 当类中包含许多行为,这些行为以多个条件语句的形式存在
- 当算法的选择依赖于调用者的上下文时
- 可以在运行时切换算法
- 将算法的实现与使用算法的代码隔离
- 用组合代替继承,更加灵活
- 符合开闭原则,增加新策略无需修改上下文
- 客户端必须知道所有策略类及其区别
- 增加了对象数量
- 如果策略很少且很少改变,使用此模式可能增加不必要的复杂性
策略模式结构
策略模式包含以下几个角色:
- 策略(Strategy):定义所有支持的算法的公共接口
- 具体策略(ConcreteStrategy):实现策略接口的具体算法
- 上下文(Context):维护一个策略引用,使用该引用调用具体策略
策略模式实现
下面是一个典型的策略模式实现:
1// 策略接口2public interface PaymentStrategy {3 void pay(int amount);4}56// 具体策略A7public class CreditCardPayment implements PaymentStrategy {8 private String cardNumber;9 private String name;10 private String cvv;11 private String dateOfExpiry;12 13 public CreditCardPayment(String cardNumber, String name, String cvv, String dateOfExpiry) {14 this.cardNumber = cardNumber;15 this.name = name;16 this.cvv = cvv;17 this.dateOfExpiry = dateOfExpiry;18 }19 20 @Override21 public void pay(int amount) {22 System.out.println(amount + " 元已通过信用卡支付");23 }24}2526// 具体策略B27public class PayPalPayment implements PaymentStrategy {28 private String emailId;29 private String password;30 31 public PayPalPayment(String email, String password) {32 this.emailId = email;33 this.password = password;34 }35 36 @Override37 public void pay(int amount) {38 System.out.println(amount + " 元已通过PayPal支付");39 }40}4142// 具体策略C43public class WeChatPayment implements PaymentStrategy {44 private String id;45 46 public WeChatPayment(String id) {47 this.id = id;48 }49 50 @Override51 public void pay(int amount) {52 System.out.println(amount + " 元已通过微信支付");53 }54}5556// 上下文57public class ShoppingCart {58 private PaymentStrategy paymentStrategy;59 60 public void setPaymentStrategy(PaymentStrategy paymentStrategy) {61 this.paymentStrategy = paymentStrategy;62 }63 64 public void checkout(int amount) {65 paymentStrategy.pay(amount);66 }67}6869// 使用示例70public class StrategyDemo {71 public static void main(String[] args) {72 ShoppingCart cart = new ShoppingCart();73 74 // 用户选择信用卡支付75 cart.setPaymentStrategy(new CreditCardPayment(76 "1234567890123456", "张三", "123", "12/25"));77 cart.checkout(1000);78 79 // 用户选择PayPal支付80 cart.setPaymentStrategy(new PayPalPayment("zhangsan@example.com", "password"));81 cart.checkout(500);82 83 // 用户选择微信支付84 cart.setPaymentStrategy(new WeChatPayment("zhangsan123"));85 cart.checkout(300);86 }87}策略模式变体
- 函数式策略
- 策略工厂
使用Java 8函数式接口和Lambda表达式可以简化策略模式的实现:
1// 使用函数式接口定义策略2public interface DiscountStrategy {3 double applyDiscount(double amount);4}56public class PriceCalculator {7 private DiscountStrategy discountStrategy;8 9 public void setDiscountStrategy(DiscountStrategy discountStrategy) {10 this.discountStrategy = discountStrategy;11 }12 13 public double calculateFinalPrice(double price) {14 return discountStrategy.applyDiscount(price);15 }16}1718// 使用Lambda表达式作为策略19public class FunctionalStrategyDemo {20 public static void main(String[] args) {21 PriceCalculator calculator = new PriceCalculator();22 23 // 无折扣策略24 calculator.setDiscountStrategy(amount -> amount);25 System.out.println("原价: " + calculator.calculateFinalPrice(100));26 27 // 固定金额折扣策略28 calculator.setDiscountStrategy(amount -> amount - 20);29 System.out.println("满减20: " + calculator.calculateFinalPrice(100));30 31 // 百分比折扣策略32 calculator.setDiscountStrategy(amount -> amount * 0.8);33 System.out.println("8折优惠: " + calculator.calculateFinalPrice(100));34 35 // 条件折扣策略36 calculator.setDiscountStrategy(amount -> 37 amount > 200 ? amount * 0.8 : amount * 0.9);38 System.out.println("满200打8折,否则9折: " + calculator.calculateFinalPrice(250));39 }40}结合工厂模式,可以根据条件动态选择合适的策略:
1// 策略接口2public interface TaxStrategy {3 double calculateTax(double income);4}56// 具体策略7public class PersonalTaxStrategy implements TaxStrategy {8 @Override9 public double calculateTax(double income) {10 return income * 0.1; // 个人所得税10%11 }12}1314public class CorporateTaxStrategy implements TaxStrategy {15 @Override16 public double calculateTax(double income) {17 return income * 0.25; // 企业所得税25%18 }19}2021public class NonProfitTaxStrategy implements TaxStrategy {22 @Override23 public double calculateTax(double income) {24 return income * 0.05; // 非盈利组织5%25 }26}2728// 策略工厂29public class TaxStrategyFactory {30 public static TaxStrategy createStrategy(String type) {31 switch (type.toLowerCase()) {32 case "personal":33 return new PersonalTaxStrategy();34 case "corporate":35 return new CorporateTaxStrategy();36 case "nonprofit":37 return new NonProfitTaxStrategy();38 default:39 throw new IllegalArgumentException("未知的税务类型: " + type);40 }41 }42}4344// 上下文类使用工厂45public class TaxCalculator {46 public double calculateTax(String type, double income) {47 TaxStrategy strategy = TaxStrategyFactory.createStrategy(type);48 return strategy.calculateTax(income);49 }50}5152// 使用示例53public class StrategyFactoryDemo {54 public static void main(String[] args) {55 TaxCalculator calculator = new TaxCalculator();56 57 System.out.println("个人税: " + calculator.calculateTax("personal", 10000));58 System.out.println("企业税: " + calculator.calculateTax("corporate", 100000));59 System.out.println("非盈利组织税: " + calculator.calculateTax("nonprofit", 50000));60 }61}策略模式应用实例
- 排序策略
- 文本压缩策略
1// 排序策略接口2public interface SortStrategy<T> {3 List<T> sort(List<T> dataset);4}56// 具体排序策略7public class QuickSortStrategy<T extends Comparable<T>> implements SortStrategy<T> {8 @Override9 public List<T> sort(List<T> dataset) {10 // 快速排序实现11 System.out.println("使用快速排序");12 List<T> result = new ArrayList<>(dataset);13 // 排序逻辑...14 return result;15 }16}1718public class MergeSortStrategy<T extends Comparable<T>> implements SortStrategy<T> {19 @Override20 public List<T> sort(List<T> dataset) {21 // 归并排序实现22 System.out.println("使用归并排序");23 List<T> result = new ArrayList<>(dataset);24 // 排序逻辑...25 return result;26 }27}2829public class BubbleSortStrategy<T extends Comparable<T>> implements SortStrategy<T> {30 @Override31 public List<T> sort(List<T> dataset) {32 // 冒泡排序实现33 System.out.println("使用冒泡排序");34 List<T> result = new ArrayList<>(dataset);35 // 排序逻辑...36 return result;37 }38}3940// 上下文类41public class Sorter<T extends Comparable<T>> {42 private SortStrategy<T> strategy;43 44 public void setSortStrategy(SortStrategy<T> strategy) {45 this.strategy = strategy;46 }47 48 public List<T> sort(List<T> dataset) {49 return strategy.sort(dataset);50 }51}5253// 基于数据特性自动选择排序策略的增强上下文54public class SmartSorter<T extends Comparable<T>> {55 private SortStrategy<T> quickSort = new QuickSortStrategy<>();56 private SortStrategy<T> mergeSort = new MergeSortStrategy<>();57 private SortStrategy<T> bubbleSort = new BubbleSortStrategy<>();58 59 public List<T> sort(List<T> dataset) {60 // 根据数据大小选择合适的排序算法61 if (dataset.size() <= 10) {62 return bubbleSort.sort(dataset);63 } else if (dataset.size() <= 1000) {64 return quickSort.sort(dataset);65 } else {66 return mergeSort.sort(dataset);67 }68 }69}1// 压缩策略接口2public interface CompressionStrategy {3 byte[] compress(String data);4 String decompress(byte[] data);5}67// GZIP压缩8public class GZIPCompressionStrategy implements CompressionStrategy {9 @Override10 public byte[] compress(String data) {11 try (ByteArrayOutputStream bos = new ByteArrayOutputStream();12 GZIPOutputStream gzipOS = new GZIPOutputStream(bos)) {13 gzipOS.write(data.getBytes("UTF-8"));14 gzipOS.close();15 return bos.toByteArray();16 } catch (IOException e) {17 throw new RuntimeException(e);18 }19 }20 21 @Override22 public String decompress(byte[] data) {23 try (GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(data));24 ByteArrayOutputStream bos = new ByteArrayOutputStream()) {25 byte[] buffer = new byte[1024];26 int len;27 while ((len = gis.read(buffer)) != -1) {28 bos.write(buffer, 0, len);29 }30 return bos.toString("UTF-8");31 } catch (IOException e) {32 throw new RuntimeException(e);33 }34 }35}3637// ZIP压缩38public class ZIPCompressionStrategy implements CompressionStrategy {39 @Override40 public byte[] compress(String data) {41 // ZIP压缩实现...42 return new byte[0]; // 简化示例43 }44 45 @Override46 public String decompress(byte[] data) {47 // ZIP解压缩实现...48 return ""; // 简化示例49 }50}5152// 不压缩(用于小文件或已压缩内容)53public class NoCompressionStrategy implements CompressionStrategy {54 @Override55 public byte[] compress(String data) {56 return data.getBytes(StandardCharsets.UTF_8);57 }58 59 @Override60 public String decompress(byte[] data) {61 return new String(data, StandardCharsets.UTF_8);62 }63}6465// 上下文类66public class Compressor {67 private CompressionStrategy strategy;68 69 public Compressor(CompressionStrategy strategy) {70 this.strategy = strategy;71 }72 73 public void setStrategy(CompressionStrategy strategy) {74 this.strategy = strategy;75 }76 77 public byte[] compress(String data) {78 return strategy.compress(data);79 }80 81 public String decompress(byte[] data) {82 return strategy.decompress(data);83 }84}8586// 使用示例87public class CompressionExample {88 public static void main(String[] args) {89 String data = "这是一段需要压缩的文本,可能很长...";90 91 // 使用GZIP压缩92 Compressor gzipCompressor = new Compressor(new GZIPCompressionStrategy());93 byte[] compressed = gzipCompressor.compress(data);94 System.out.println("GZIP压缩后大小: " + compressed.length);95 96 // 解压缩97 String decompressed = gzipCompressor.decompress(compressed);98 System.out.println("解压后内容: " + decompressed);99 100 // 切换为无压缩策略101 gzipCompressor.setStrategy(new NoCompressionStrategy());102 byte[] noCompression = gzipCompressor.compress(data);103 System.out.println("不压缩大小: " + noCompression.length);104 }105}策略模式与其他模式比较
| 模式 | 区别 | 组合使用场景 |
|---|---|---|
| 策略模式 vs 状态模式 | 状态模式允许对象随着状态改变而改变行为,策略模式专注于不同算法的互换 | 策略模式可以用来实现状态模式中的不同状态行为 |
| 策略模式 vs 命令模式 | 命令模式封装执行行为的请求,策略模式封装不同的算法实现 | 命令可以使用不同的策略执行请求 |
| 策略模式 vs 模板方法模式 | 模板方法使用继承来更改算法的部分内容,策略使用组合来更换整个算法 | 模板方法的钩子方法可以使用策略模式提供不同实现 |
策略模式实现要点
-
上下文和策略的关系:上下文不应过度依赖具体策略的内部细节
-
策略的动态选择:基于条件自动选择策略可以提高灵活性
java1public PaymentStrategy chooseStrategy(String paymentType, Map<String, String> paymentDetails) {2 switch(paymentType) {3 case "CREDIT_CARD":4 return new CreditCardPayment(5 paymentDetails.get("cardNumber"),6 paymentDetails.get("name"),7 paymentDetails.get("cvv"),8 paymentDetails.get("expiryDate"));9 case "PAYPAL":10 return new PayPalPayment(11 paymentDetails.get("email"),12 paymentDetails.get("password"));13 case "WECHAT":14 return new WeChatPayment(15 paymentDetails.get("id"));16 default:17 throw new IllegalArgumentException("不支持的支付类型");18 }19} -
策略接口定义:策略接口应该保持简单,仅包含必要方法
java1// 好的做法:接口简洁2public interface ValidationStrategy {3 boolean validate(String text);4}56// 避免的做法:接口过于复杂7public interface ComplexValidationStrategy {8 boolean validate(String text);9 boolean validateFormat(String format);10 void setValidationRules(List<Rule> rules);11 List<String> getValidationErrors();12 // 其他不必要的方法...13} -
性能考虑:避免频繁创建和切换策略对象
- 使用策略模式代替复杂的条件语句
- 当有多个算法只有微小变化时,考虑使用lambda表达式或匿名类
- 结合工厂模式可以隐藏客户端对具体策略的直接依赖
- 策略对象应该是无状态的,以便可以在多个上下文间共享
- 在合适的情况下使用枚举实现简单的策略模式
命令模式 (Command Pattern)
命令模式是一种行为设计模式,它将请求封装为一个对象,从而可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
- 适用场景
- 优点
- 缺点
- 需要将操作参数化时
- 需要将操作放入队列中、操作的执行需要延迟或者操作需要远程执行时
- 需要支持撤销操作时
- 需要支持事务操作,将一系列操作作为一个整体执行或回滚
- 需要实现操作的日志记录、审计或历史记录
- 解耦命令发送者和接收者
- 可以轻松添加新命令
- 支持撤销/重做操作
- 支持宏命令(组合命令)
- 支持命令队列和延迟执行
- 可能导致系统中有过多的具体命令类
- 复杂命令可能需要保存更多状态来支持撤销
- 如果命令接口过于复杂,可能增加客户端负担
命令模式结构
命令模式包含以下几个角色:
- 命令(Command):声明执行操作的接口,通常包含execute()方法和可选的undo()方法
- 具体命令(ConcreteCommand):定义命令与接收者之间的绑定,实现execute()方法,调用接收者的相关操作
- 调用者(Invoker):要求命令执行请求
- 接收者(Receiver):知道如何实施与执行请求相关的操作
- 客户端(Client):创建具体命令对象并设置其接收者
命令模式实现
下面是一个命令模式的基本实现:
1// 命令接口2public interface Command {3 void execute();4 void undo();5}67// 接收者8public class Light {9 private boolean isOn = false;10 private String location;11 12 public Light(String location) {13 this.location = location;14 }15 16 public void turnOn() {17 isOn = true;18 System.out.println(location + " 灯已打开");19 }20 21 public void turnOff() {22 isOn = false;23 System.out.println(location + " 灯已关闭");24 }25 26 public boolean isOn() {27 return isOn;28 }29}3031// 具体命令32public class LightOnCommand implements Command {33 private Light light;34 35 public LightOnCommand(Light light) {36 this.light = light;37 }38 39 @Override40 public void execute() {41 light.turnOn();42 }43 44 @Override45 public void undo() {46 light.turnOff();47 }48}4950public class LightOffCommand implements Command {51 private Light light;52 53 public LightOffCommand(Light light) {54 this.light = light;55 }56 57 @Override58 public void execute() {59 light.turnOff();60 }61 62 @Override63 public void undo() {64 light.turnOn();65 }66}6768// 调用者69public class RemoteControl {70 private Command command;71 72 public void setCommand(Command command) {73 this.command = command;74 }75 76 public void pressButton() {77 command.execute();78 }79 80 public void pressUndoButton() {81 command.undo();82 }83}8485// 客户端86public class CommandDemo {87 public static void main(String[] args) {88 // 创建接收者89 Light livingRoomLight = new Light("客厅");90 91 // 创建命令92 Command livingRoomLightOn = new LightOnCommand(livingRoomLight);93 Command livingRoomLightOff = new LightOffCommand(livingRoomLight);94 95 // 创建调用者96 RemoteControl remote = new RemoteControl();97 98 // 执行命令99 remote.setCommand(livingRoomLightOn);100 remote.pressButton(); // 打开客厅灯101 102 remote.setCommand(livingRoomLightOff);103 remote.pressButton(); // 关闭客厅灯104 105 remote.pressUndoButton(); // 撤销关灯,灯重新打开106 }107}命令模式变体
- 宏命令
- 命令队列
- 命令历史
宏命令允许将多个命令组合成一个命令:
1// 宏命令 - 组合多个命令2public class MacroCommand implements Command {3 private List<Command> commands;4 5 public MacroCommand() {6 this.commands = new ArrayList<>();7 }8 9 public void addCommand(Command command) {10 commands.add(command);11 }12 13 @Override14 public void execute() {15 for (Command command : commands) {16 command.execute();17 }18 }19 20 @Override21 public void undo() {22 // 逆序撤销23 for (int i = commands.size() - 1; i >= 0; i--) {24 commands.get(i).undo();25 }26 }27}2829// 使用宏命令30public static void main(String[] args) {31 Light livingRoomLight = new Light("客厅");32 Light kitchenLight = new Light("厨房");33 34 Command livingRoomLightOn = new LightOnCommand(livingRoomLight);35 Command kitchenLightOn = new LightOnCommand(kitchenLight);36 37 MacroCommand allLightsOn = new MacroCommand();38 allLightsOn.addCommand(livingRoomLightOn);39 allLightsOn.addCommand(kitchenLightOn);40 41 RemoteControl remote = new RemoteControl();42 43 // 一次性打开所有灯44 remote.setCommand(allLightsOn);45 remote.pressButton();46 47 // 一次性关闭所有灯48 remote.pressUndoButton();49}命令队列允许命令的异步执行或延迟执行:
1// 命令队列2public class CommandQueue {3 private Queue<Command> commandQueue = new LinkedList<>();4 5 public void addCommand(Command command) {6 commandQueue.offer(command);7 }8 9 public void processCommands() {10 while (!commandQueue.isEmpty()) {11 Command command = commandQueue.poll();12 command.execute();13 }14 }15}1617// 使用命令队列18public static void main(String[] args) {19 Light livingRoomLight = new Light("客厅");20 Command livingRoomLightOn = new LightOnCommand(livingRoomLight);21 Command livingRoomLightOff = new LightOffCommand(livingRoomLight);22 23 CommandQueue queue = new CommandQueue();24 queue.addCommand(livingRoomLightOn);25 queue.addCommand(livingRoomLightOff);26 queue.addCommand(livingRoomLightOn);27 28 // 批量处理命令29 queue.processCommands();30}维护命令历史,支持多步撤销:
1// 带历史记录的调用者2public class HistoryRemoteControl {3 private Command command;4 private Stack<Command> history = new Stack<>();5 6 public void setCommand(Command command) {7 this.command = command;8 }9 10 public void pressButton() {11 if (command != null) {12 command.execute();13 history.push(command);14 }15 }16 17 public void pressUndoButton() {18 if (!history.isEmpty()) {19 Command lastCommand = history.pop();20 lastCommand.undo();21 }22 }23}2425// 使用带历史的遥控器26public static void main(String[] args) {27 Light livingRoomLight = new Light("客厅");28 Light kitchenLight = new Light("厨房");29 30 Command livingRoomLightOn = new LightOnCommand(livingRoomLight);31 Command kitchenLightOn = new LightOnCommand(kitchenLight);32 33 HistoryRemoteControl remote = new HistoryRemoteControl();34 35 remote.setCommand(livingRoomLightOn);36 remote.pressButton();37 38 remote.setCommand(kitchenLightOn);39 remote.pressButton();40 41 // 撤销打开厨房灯42 remote.pressUndoButton();43 44 // 撤销打开客厅灯45 remote.pressUndoButton();46}命令模式应用实例
- 编辑器操作
- 事务管理
1// 文本编辑器中的命令模式2public class Document {3 private StringBuilder content = new StringBuilder();4 5 public void insert(int position, String text) {6 if (position <= content.length()) {7 content.insert(position, text);8 System.out.println("已插入文本:" + text);9 }10 }11 12 public String delete(int position, int length) {13 if (position < content.length() && position + length <= content.length()) {14 String deletedText = content.substring(position, position + length);15 content.delete(position, position + length);16 System.out.println("已删除文本:" + deletedText);17 return deletedText;18 }19 return "";20 }21 22 public String getContent() {23 return content.toString();24 }25}2627public interface EditorCommand extends Command {28 // Command接口中已定义execute和undo方法29}3031public class InsertCommand implements EditorCommand {32 private Document document;33 private int position;34 private String text;35 36 public InsertCommand(Document document, int position, String text) {37 this.document = document;38 this.position = position;39 this.text = text;40 }41 42 @Override43 public void execute() {44 document.insert(position, text);45 }46 47 @Override48 public void undo() {49 document.delete(position, text.length());50 }51}5253public class DeleteCommand implements EditorCommand {54 private Document document;55 private int position;56 private int length;57 private String deletedText; // 用于撤销58 59 public DeleteCommand(Document document, int position, int length) {60 this.document = document;61 this.position = position;62 this.length = length;63 }64 65 @Override66 public void execute() {67 deletedText = document.delete(position, length);68 }69 70 @Override71 public void undo() {72 document.insert(position, deletedText);73 }74}7576public class Editor {77 private Document document = new Document();78 private Stack<EditorCommand> undoStack = new Stack<>();79 private Stack<EditorCommand> redoStack = new Stack<>();80 81 public void executeCommand(EditorCommand command) {82 command.execute();83 undoStack.push(command);84 redoStack.clear(); // 执行新命令后清空重做栈85 }86 87 public void undo() {88 if (!undoStack.isEmpty()) {89 EditorCommand command = undoStack.pop();90 command.undo();91 redoStack.push(command);92 }93 }94 95 public void redo() {96 if (!redoStack.isEmpty()) {97 EditorCommand command = redoStack.pop();98 command.execute();99 undoStack.push(command);100 }101 }102 103 public String getContent() {104 return document.getContent();105 }106}107108// 使用示例109public static void main(String[] args) {110 Editor editor = new Editor();111 112 // 插入文本113 editor.executeCommand(new InsertCommand(editor.document, 0, "Hello, "));114 editor.executeCommand(new InsertCommand(editor.document, 7, "World!"));115 116 System.out.println("当前内容: " + editor.getContent()); // Hello, World!117 118 // 删除部分文本119 editor.executeCommand(new DeleteCommand(editor.document, 7, 6));120 121 System.out.println("删除后: " + editor.getContent()); // Hello, !122 123 // 撤销删除124 editor.undo();125 System.out.println("撤销后: " + editor.getContent()); // Hello, World!126 127 // 重做删除128 editor.redo();129 System.out.println("重做后: " + editor.getContent()); // Hello, !130}1// 数据库事务中的命令模式2public class BankAccount {3 private String accountId;4 private double balance;5 6 public BankAccount(String accountId, double initialBalance) {7 this.accountId = accountId;8 this.balance = initialBalance;9 }10 11 public void deposit(double amount) {12 balance += amount;13 System.out.println("账户 " + accountId + " 存入 " + amount + ",余额: " + balance);14 }15 16 public boolean withdraw(double amount) {17 if (balance >= amount) {18 balance -= amount;19 System.out.println("账户 " + accountId + " 取出 " + amount + ",余额: " + balance);20 return true;21 } else {22 System.out.println("账户 " + accountId + " 余额不足,无法取出 " + amount);23 return false;24 }25 }26}2728// 转账命令29public class TransferCommand implements Command {30 private BankAccount sourceAccount;31 private BankAccount targetAccount;32 private double amount;33 private boolean executed = false;34 35 public TransferCommand(BankAccount sourceAccount, BankAccount targetAccount, double amount) {36 this.sourceAccount = sourceAccount;37 this.targetAccount = targetAccount;38 this.amount = amount;39 }40 41 @Override42 public void execute() {43 if (sourceAccount.withdraw(amount)) {44 targetAccount.deposit(amount);45 executed = true;46 }47 }48 49 @Override50 public void undo() {51 if (executed) {52 targetAccount.withdraw(amount);53 sourceAccount.deposit(amount);54 executed = false;55 }56 }57}5859// 事务管理器60public class TransactionManager {61 private List<Command> commands = new ArrayList<>();62 63 public void addCommand(Command cmd) {64 commands.add(cmd);65 }66 67 public boolean executeTransaction() {68 List<Command> executedCommands = new ArrayList<>();69 try {70 // 执行所有命令71 for (Command cmd : commands) {72 cmd.execute();73 executedCommands.add(cmd);74 }75 return true;76 } catch (Exception e) {77 // 回滚已执行的命令78 System.out.println("事务失败,开始回滚...");79 for (int i = executedCommands.size() - 1; i >= 0; i--) {80 executedCommands.get(i).undo();81 }82 return false;83 }84 }85}8687// 使用示例88public static void main(String[] args) {89 BankAccount accountA = new BankAccount("A001", 1000);90 BankAccount accountB = new BankAccount("B001", 500);91 92 TransactionManager transactionManager = new TransactionManager();93 94 // 创建两个转账命令95 Command transfer1 = new TransferCommand(accountA, accountB, 300);96 Command transfer2 = new TransferCommand(accountB, accountA, 100);97 98 transactionManager.addCommand(transfer1);99 transactionManager.addCommand(transfer2);100 101 // 执行事务102 boolean success = transactionManager.executeTransaction();103 System.out.println("事务执行" + (success ? "成功" : "失败"));104}命令模式与其他模式比较
| 模式 | 区别 | 组合使用场景 |
|---|---|---|
| 命令模式 vs 策略模式 | 命令模式关注的是请求的封装,策略模式关注的是算法的封装 | 命令可以使用不同策略执行请求 |
| 命令模式 vs 备忘录模式 | 命令可以存储系统状态的变化过程,备忘录存储的是系统在某一时刻的完整状态 | 可以使用备忘录模式来存储命令执行前的状态 |
| 命令模式 vs 原型模式 | 命令对象可以通过克隆来创建副本 | 通过原型模式复制命令对象以支持命令的重用 |
命令模式实现要点
-
智能命令与简单命令:
- 简单命令只负责调用接收者的方法
- 智能命令自己包含业务逻辑,不依赖接收者
-
命令参数化:
java1// 参数化命令2public class ParameterizedCommand implements Command {3 private Receiver receiver;4 private Object[] parameters;56 public ParameterizedCommand(Receiver receiver, Object... parameters) {7 this.receiver = receiver;8 this.parameters = parameters;9 }1011 @Override12 public void execute() {13 // 根据参数执行不同操作14 receiver.action(parameters);15 }1617 @Override18 public void undo() {19 // 撤销操作20 }21} -
命令序列化:
java1// 可序列化命令,支持持久化2public class SerializableCommand implements Command, Serializable {3 private static final long serialVersionUID = 1L;4 private transient Receiver receiver; // transient表示不序列化此字段5 private String actionName;6 private Map<String, Serializable> parameters;78 // 构造方法和execute/undo实现9 // ...1011 // 保存命令到文件12 public static void saveCommand(Command command, String filename) throws IOException {13 try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename))) {14 out.writeObject(command);15 }16 }1718 // 从文件加载命令19 public static Command loadCommand(String filename) throws IOException, ClassNotFoundException {20 try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {21 return (Command) in.readObject();22 }23 }24} -
命令模式与函数式编程:
java1// 使用函数式接口2@FunctionalInterface3public interface ActionCommand {4 void execute();56 // Java 8默认方法7 default ActionCommand andThen(ActionCommand after) {8 return () -> {9 this.execute();10 after.execute();11 };12 }13}1415// 使用示例16public static void main(String[] args) {17 Light light = new Light("客厅");1819 ActionCommand turnOn = light::turnOn;20 ActionCommand turnOff = light::turnOff;2122 // 组合命令23 ActionCommand flashLight = turnOn.andThen(turnOff).andThen(turnOn).andThen(turnOff);2425 // 执行命令26 flashLight.execute();27}
- 为命令定义清晰的生命周期:创建、执行、撤销和销毁
- 考虑命令对象的可重用性,避免重复创建相同的命令
- 对于复杂操作,使用宏命令组合多个命令
- 在需要持久化、远程执行或延迟执行操作时,考虑使用命令模式
- 命令模式特别适合实现撤销/重做、事务处理和操作日志
状态模式 (State Pattern)
状态模式是一种行为设计模式,它允许对象在内部状态改变时改变它的行为,使对象看起来好像修改了它的类。状态模式是对象行为随状态改变而改变的场景的解决方案。
- 适用场景
- 优点
- 缺点
- 对象的行为取决于它的状态,且必须在运行时根据状态改变其行为
- 代码中包含大量与对象状态有关的条件语句,如多个if-else或switch-case
- 状态转换规则复杂且频繁变化
- 需要消除"状态判断"的条件语句时
- 单一职责原则,将状态特定的行为局部化
- 开闭原则,无需修改上下文就能引入新状态
- 消除了庞大的条件判断语句
- 使状态转换更加明确
- 状态对象可以共享
- 如果状态机只有很少的几个状态或很少变化,使用此模式可能过度设计
- 会增加类的数量
- 如果状态转换逻辑分散在状态类中,可能难以掌握整个状态机的逻辑
状态模式结构
状态模式包含以下几个角色:
- 上下文(Context):维护一个对当前状态对象的引用,并将与状态相关的操作委托给当前状态对象
- 状态(State):定义一个接口,封装与上下文的特定状态相关的行为
- 具体状态(ConcreteState):实现状态接口,提供与上下文特定状态相关的行为
状态模式实现
下面是一个典型的状态模式实现:
1// 状态接口2public interface State {3 void handle(Context context);4 String getStateName();5}67// 具体状态A8public class ConcreteStateA implements State {9 @Override10 public void handle(Context context) {11 System.out.println("当前在状态A,处理完毕后切换到状态B");12 context.setState(new ConcreteStateB());13 }14 15 @Override16 public String getStateName() {17 return "State A";18 }19}2021// 具体状态B22public class ConcreteStateB implements State {23 @Override24 public void handle(Context context) {25 System.out.println("当前在状态B,处理完毕后切换到状态A");26 context.setState(new ConcreteStateA());27 }28 29 @Override30 public String getStateName() {31 return "State B";32 }33}3435// 上下文36public class Context {37 private State currentState;38 39 public Context() {40 // 默认状态41 currentState = new ConcreteStateA();42 }43 44 public void setState(State state) {45 this.currentState = state;46 System.out.println("状态变为: " + state.getStateName());47 }48 49 public void request() {50 currentState.handle(this);51 }52}5354// 客户端55public class StatePatternDemo {56 public static void main(String[] args) {57 Context context = new Context();58 59 context.request(); // A -> B60 context.request(); // B -> A61 context.request(); // A -> B62 }63}状态模式变体
- 状态驱动转换
- 表驱动状态机
- 状态模式与枚举
状态自身决定下一个状态,适合状态转换逻辑复杂的场景:
1// 状态接口2public interface State {3 void handle(Context context);4}56// 具体状态实现,自己负责状态转换7public class PlayingState implements State {8 @Override9 public void handle(Context context) {10 System.out.println("播放中:可以暂停或停止");11 12 // 模拟用户点击暂停按钮13 if (userClickedPause()) {14 context.setState(new PausedState());15 }16 17 // 模拟播放结束18 if (playbackFinished()) {19 context.setState(new StoppedState());20 }21 }22}使用状态转换表来管理状态转换,适合转换规则频繁变化的场景:
1// 事件枚举2public enum Event {3 PLAY, PAUSE, STOP4}56// 状态转换表7public class StateMachine {8 private Map<State, Map<Event, State>> transitions = new HashMap<>();9 private State currentState;10 11 public StateMachine(State initialState) {12 this.currentState = initialState;13 }14 15 public void addTransition(State from, Event event, State to) {16 if (!transitions.containsKey(from)) {17 transitions.put(from, new HashMap<>());18 }19 transitions.get(from).put(event, to);20 }21 22 public void processEvent(Event event) {23 Map<Event, State> stateTransitions = transitions.get(currentState);24 if (stateTransitions.containsKey(event)) {25 State nextState = stateTransitions.get(event);26 System.out.println("从 " + currentState.getStateName() + 27 " 转换到 " + nextState.getStateName());28 currentState = nextState;29 currentState.enter();30 } else {31 System.out.println("在状态 " + currentState.getStateName() + 32 " 不能处理事件 " + event);33 }34 }35}3637// 使用示例38public void setupMusicPlayer() {39 State stopped = new StoppedState();40 State playing = new PlayingState();41 State paused = new PausedState();42 43 StateMachine playerStateMachine = new StateMachine(stopped);44 45 playerStateMachine.addTransition(stopped, Event.PLAY, playing);46 playerStateMachine.addTransition(playing, Event.PAUSE, paused);47 playerStateMachine.addTransition(playing, Event.STOP, stopped);48 playerStateMachine.addTransition(paused, Event.PLAY, playing);49 playerStateMachine.addTransition(paused, Event.STOP, stopped);50 51 // 处理事件52 playerStateMachine.processEvent(Event.PLAY); // 停止 -> 播放53 playerStateMachine.processEvent(Event.PAUSE); // 播放 -> 暂停54 playerStateMachine.processEvent(Event.PLAY); // 暂停 -> 播放55 playerStateMachine.processEvent(Event.STOP); // 播放 -> 停止56}使用枚举实现状态模式,适合状态和行为相对固定的场景:
1// 使用枚举实现状态模式2public enum TrafficLightState {3 RED {4 @Override5 public void handle(TrafficLight trafficLight) {6 System.out.println("红灯亮起,禁止通行");7 try {8 Thread.sleep(5000); // 红灯持续5秒9 } catch (InterruptedException e) {10 Thread.currentThread().interrupt();11 }12 trafficLight.setState(GREEN);13 }14 },15 YELLOW {16 @Override17 public void handle(TrafficLight trafficLight) {18 System.out.println("黄灯亮起,准备停止");19 try {20 Thread.sleep(2000); // 黄灯持续2秒21 } catch (InterruptedException e) {22 Thread.currentThread().interrupt();23 }24 trafficLight.setState(RED);25 }26 },27 GREEN {28 @Override29 public void handle(TrafficLight trafficLight) {30 System.out.println("绿灯亮起,可以通行");31 try {32 Thread.sleep(5000); // 绿灯持续5秒33 } catch (InterruptedException e) {34 Thread.currentThread().interrupt();35 }36 trafficLight.setState(YELLOW);37 }38 };39 40 public abstract void handle(TrafficLight trafficLight);41}4243public class TrafficLight {44 private TrafficLightState state = TrafficLightState.RED;45 46 public void setState(TrafficLightState state) {47 this.state = state;48 }49 50 public void change() {51 state.handle(this);52 }53}5455// 使用示例56public static void main(String[] args) {57 TrafficLight trafficLight = new TrafficLight();58 59 for (int i = 0; i < 3; i++) {60 trafficLight.change();61 }62}状态模式应用实例
- 订单状态管理
- 游戏角色状态
1// 订单状态管理中的状态模式2public interface OrderState {3 void processOrder(Order order);4 void cancelOrder(Order order);5 void payOrder(Order order);6 void shipOrder(Order order);7 void deliverOrder(Order order);8}910// 新建订单状态11public class NewOrderState implements OrderState {12 @Override13 public void processOrder(Order order) {14 System.out.println("订单已创建,等待付款");15 }16 17 @Override18 public void cancelOrder(Order order) {19 System.out.println("取消新订单");20 order.setState(new CancelledOrderState());21 }22 23 @Override24 public void payOrder(Order order) {25 System.out.println("订单已付款");26 order.setState(new PaidOrderState());27 }28 29 @Override30 public void shipOrder(Order order) {31 System.out.println("错误:未付款订单不能发货");32 }33 34 @Override35 public void deliverOrder(Order order) {36 System.out.println("错误:未发货订单不能交付");37 }38}3940// 已付款订单状态41public class PaidOrderState implements OrderState {42 @Override43 public void processOrder(Order order) {44 System.out.println("订单已付款,等待发货");45 }46 47 @Override48 public void cancelOrder(Order order) {49 System.out.println("取消已付款订单,需要退款");50 order.setState(new CancelledOrderState());51 // 处理退款逻辑52 }53 54 @Override55 public void payOrder(Order order) {56 System.out.println("错误:订单已支付");57 }58 59 @Override60 public void shipOrder(Order order) {61 System.out.println("订单已发货");62 order.setState(new ShippedOrderState());63 }64 65 @Override66 public void deliverOrder(Order order) {67 System.out.println("错误:未发货订单不能交付");68 }69}7071// 已发货订单状态72public class ShippedOrderState implements OrderState {73 @Override74 public void processOrder(Order order) {75 System.out.println("订单正在运送中");76 }77 78 @Override79 public void cancelOrder(Order order) {80 System.out.println("错误:已发货订单不能取消");81 }82 83 @Override84 public void payOrder(Order order) {85 System.out.println("错误:订单已支付");86 }87 88 @Override89 public void shipOrder(Order order) {90 System.out.println("错误:订单已发货");91 }92 93 @Override94 public void deliverOrder(Order order) {95 System.out.println("订单已交付");96 order.setState(new DeliveredOrderState());97 }98}99100// 已交付订单状态101public class DeliveredOrderState implements OrderState {102 // 实现方法...103}104105// 已取消订单状态106public class CancelledOrderState implements OrderState {107 // 实现方法...108}109110// 订单上下文111public class Order {112 private String orderNumber;113 private OrderState state;114 115 public Order(String orderNumber) {116 this.orderNumber = orderNumber;117 this.state = new NewOrderState();118 }119 120 public void setState(OrderState state) {121 this.state = state;122 }123 124 public void processOrder() {125 state.processOrder(this);126 }127 128 public void cancelOrder() {129 state.cancelOrder(this);130 }131 132 public void payOrder() {133 state.payOrder(this);134 }135 136 public void shipOrder() {137 state.shipOrder(this);138 }139 140 public void deliverOrder() {141 state.deliverOrder(this);142 }143}144145// 使用示例146public static void main(String[] args) {147 Order order = new Order("ORD-12345");148 order.processOrder(); // 订单已创建,等待付款149 150 order.payOrder(); // 订单已付款151 order.processOrder(); // 订单已付款,等待发货152 153 order.shipOrder(); // 订单已发货154 order.processOrder(); // 订单正在运送中155 156 order.deliverOrder(); // 订单已交付157}1// 游戏角色状态管理2public interface CharacterState {3 void handleInput(Character character, Input input);4 void update(Character character);5 String getStateName();6}78// 站立状态9public class StandingState implements CharacterState {10 @Override11 public void handleInput(Character character, Input input) {12 if (input == Input.PRESS_LEFT || input == Input.PRESS_RIGHT) {13 character.setState(new WalkingState());14 } else if (input == Input.PRESS_UP) {15 character.setState(new JumpingState());16 } else if (input == Input.PRESS_DOWN) {17 character.setState(new DuckingState());18 }19 }20 21 @Override22 public void update(Character character) {23 // 站立时的更新逻辑24 }25 26 @Override27 public String getStateName() {28 return "站立";29 }30}3132// 行走状态33public class WalkingState implements CharacterState {34 @Override35 public void handleInput(Character character, Input input) {36 if (input == Input.RELEASE_LEFT_RIGHT) {37 character.setState(new StandingState());38 } else if (input == Input.PRESS_UP) {39 character.setState(new JumpingState());40 } else if (input == Input.PRESS_DOWN) {41 character.setState(new DuckingState());42 } else if (input == Input.PRESS_DASH) {43 character.setState(new RunningState());44 }45 }46 47 @Override48 public void update(Character character) {49 // 行走时的更新逻辑50 character.move(character.getFacingDirection(), character.getWalkSpeed());51 }52 53 @Override54 public String getStateName() {55 return "行走";56 }57}5859// 跳跃状态60public class JumpingState implements CharacterState {61 private int framesInAir = 0;62 private final int MAX_JUMP_FRAMES = 30;63 64 @Override65 public void handleInput(Character character, Input input) {66 // 跳跃中的输入处理有限67 if (input == Input.PRESS_DOWN) {68 character.setState(new DivingState());69 }70 }71 72 @Override73 public void update(Character character) {74 framesInAir++;75 76 // 跳跃物理计算77 double jumpForce = calculateJumpForce(framesInAir);78 character.setVelocityY(jumpForce);79 80 // 更新角色位置81 character.move(character.getFacingDirection(), character.getWalkSpeed());82 character.moveVertically();83 84 // 检测是否落地85 if (framesInAir >= MAX_JUMP_FRAMES || character.isOnGround()) {86 character.setState(new StandingState());87 }88 }89 90 private double calculateJumpForce(int framesInAir) {91 // 初始向上力逐渐减少,然后变为向下力(重力)92 return 10.0 - (framesInAir * 0.7);93 }94 95 @Override96 public String getStateName() {97 return "跳跃";98 }99}100101// 其他状态:蹲下、奔跑、俯冲等...102103// 游戏角色(上下文)104public class Character {105 private CharacterState state;106 private int x, y;107 private int facingDirection; // -1左,1右108 private boolean onGround;109 private double velocityY;110 private final int walkSpeed = 3;111 112 public Character(int startX, int startY) {113 this.x = startX;114 this.y = startY;115 this.state = new StandingState();116 this.facingDirection = 1;117 this.onGround = true;118 }119 120 public void setState(CharacterState state) {121 System.out.println("角色从 " + this.state.getStateName() + 122 " 状态变为 " + state.getStateName());123 this.state = state;124 }125 126 public void handleInput(Input input) {127 state.handleInput(this, input);128 }129 130 public void update() {131 state.update(this);132 }133 134 public void move(int direction, int speed) {135 this.x += direction * speed;136 }137 138 public void moveVertically() {139 this.y += velocityY;140 // 简单地检测与地面的碰撞141 if (this.y <= 0) {142 this.y = 0;143 this.onGround = true;144 } else {145 this.onGround = false;146 }147 }148 149 // Getters和Setters150 public int getFacingDirection() { return facingDirection; }151 public void setFacingDirection(int direction) { this.facingDirection = direction; }152 public boolean isOnGround() { return onGround; }153 public int getWalkSpeed() { return walkSpeed; }154 public void setVelocityY(double velocityY) { this.velocityY = velocityY; }155}156157// 输入枚举158public enum Input {159 PRESS_UP, PRESS_DOWN, PRESS_LEFT, PRESS_RIGHT,160 RELEASE_LEFT_RIGHT, PRESS_DASH161}162163// 使用示例164public static void main(String[] args) {165 Character player = new Character(100, 0);166 167 // 模拟游戏循环168 player.handleInput(Input.PRESS_RIGHT); // 开始行走169 player.update();170 171 player.handleInput(Input.PRESS_UP); // 跳跃172 player.update();173 174 // 多次更新跳跃状态175 for (int i = 0; i < 10; i++) {176 player.update();177 }178}状态模式与其他模式比较
| 模式 | 区别 | 组合使用场景 |
|---|---|---|
| 状态模式 vs 策略模式 | 状态模式关注对象状态变化引起的行为变化,策略模式专注于不同算法的互换 | 可以使用策略模式来实现不同状态下的不同算法 |
| 状态模式 vs 命令模式 | 命令模式封装执行行为的请求,策略模式封装不同的算法实现 | 可以根据当前状态选择不同的命令对象 |
| 状态模式 vs 备忘录模式 | 状态模式改变对象行为,备忘录模式保存历史状态 | 可以用备忘录记录状态对象的状态,实现状态恢复 |
状态模式实现要点
-
谁来负责状态转换:状态模式有两种实现方式:
- 上下文驱动:状态转换逻辑由上下文控制
- 状态自驱动:状态转换逻辑由状态类自身控制
-
状态共享:如果状态不包含内部状态,可以将状态对象设计为共享的:
java1// 状态对象共享2public class Context {3 // 静态共享状态对象4 private static final State STATE_A = new ConcreteStateA();5 private static final State STATE_B = new ConcreteStateB();67 private State currentState = STATE_A;89 public void setState(State state) {10 this.currentState = state;11 }1213 public void switchToStateA() {14 setState(STATE_A);15 }1617 public void switchToStateB() {18 setState(STATE_B);19 }20} -
状态历史记录:记录状态变化历史,支持状态回退:
java1public class ContextWithHistory {2 private State currentState;3 private Stack<State> history = new Stack<>();45 public void setState(State state) {6 history.push(currentState);7 currentState = state;8 }910 public void undo() {11 if (!history.isEmpty()) {12 currentState = history.pop();13 }14 }15} -
状态转换表:使用状态转换表管理复杂的状态转换规则:
java1public class StateMachineContext {2 private Map<StateEvent, State> stateTransitionTable = new HashMap<>();3 private State currentState;45 public void registerTransition(State fromState, Event event, State toState) {6 StateEvent stateEvent = new StateEvent(fromState, event);7 stateTransitionTable.put(stateEvent, toState);8 }910 public void handleEvent(Event event) {11 StateEvent stateEvent = new StateEvent(currentState, event);12 State nextState = stateTransitionTable.get(stateEvent);1314 if (nextState != null) {15 currentState = nextState;16 }17 }18}1920// 复合键:状态+事件21public class StateEvent {22 private State state;23 private Event event;2425 // 构造函数、equals和hashCode方法...26}
- 首先识别所有可能的状态和它们之间的转换关系
- 明确状态转换的触发条件
- 使用状态图或状态转换表来可视化状态机
- 对于简单状态机,可以考虑使用枚举实现
- 对于复杂状态机,将状态转换逻辑放在状态类中
- 避免在状态类中引入上下文特定的数据,保持状态类的独立性
- 当状态数量少且状态逻辑简单时,可能使用策略模式更合适
责任链模式 (Chain of Responsibility Pattern)
责任链模式是一种行为设计模式,它为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。请求沿着链传递,直到有一个接收者处理它为止。
- 适用场景
- 优点
- 缺点
- 有多个对象可以处理同一个请求,但具体由哪个对象处理需要在运行时决定
- 想要在不明确接收者的情况下向多个对象中的一个提交请求
- 处理请求的对象集合需要被动态指定
- 需要按照顺序执行多个处理器的场景
- 降低了请求发送者和接收者之间的耦合度
- 符合单一职责原则,每个处理器只关心自己能处理的请求
- 符合开闭原则,可以在不修改现有代码的情况下增加新的处理器
- 可以动态地组合和修改处理流程
- 请求可能无人处理,需要额外的兜底逻辑
- 请求处理路径难以追踪,不易调试
- 责任链过长可能导致性能问题
- 链路配置不当可能导致循环引用
责任链模式结构
责任链模式包含以下几个角色:
- 处理者(Handler):定义处理请求的接口,包括设置下一个处理者的方法
- 具体处理者(ConcreteHandler):实现处理请求的方法,如果自己无法处理,则将请求转发给后继者
- 客户端(Client):创建处理者对象并组织责任链,然后将请求发送给链的第一个处理者
责任链模式实现
下面是一个典型的责任链模式实现:
1// 请求类2public class Request {3 private String type;4 private String content;5 private int priority;6 7 public Request(String type, String content, int priority) {8 this.type = type;9 this.content = content;10 this.priority = priority;11 }12 13 // getter方法14 public String getType() { return type; }15 public String getContent() { return content; }16 public int getPriority() { return priority; }17}1819// 处理者接口20public abstract class Handler {21 protected Handler successor;22 23 public void setSuccessor(Handler successor) {24 this.successor = successor;25 }26 27 public abstract void handleRequest(Request request);28}2930// 具体处理者A31public class ConcreteHandlerA extends Handler {32 @Override33 public void handleRequest(Request request) {34 if ("TypeA".equals(request.getType())) {35 System.out.println("处理者A处理请求:" + request.getContent());36 } else if (successor != null) {37 System.out.println("处理者A无法处理,转发给下一个处理者");38 successor.handleRequest(request);39 } else {40 System.out.println("没有处理者能处理该请求");41 }42 }43}4445// 具体处理者B46public class ConcreteHandlerB extends Handler {47 @Override48 public void handleRequest(Request request) {49 if ("TypeB".equals(request.getType())) {50 System.out.println("处理者B处理请求:" + request.getContent());51 } else if (successor != null) {52 System.out.println("处理者B无法处理,转发给下一个处理者");53 successor.handleRequest(request);54 } else {55 System.out.println("没有处理者能处理该请求");56 }57 }58}5960// 具体处理者C61public class ConcreteHandlerC extends Handler {62 @Override63 public void handleRequest(Request request) {64 if (request.getPriority() > 10) {65 System.out.println("处理者C处理高优先级请求:" + request.getContent());66 } else if (successor != null) {67 System.out.println("处理者C无法处理,转发给下一个处理者");68 successor.handleRequest(request);69 } else {70 System.out.println("没有处理者能处理该请求");71 }72 }73}7475// 客户端76public class ChainOfResponsibilityDemo {77 public static void main(String[] args) {78 // 创建处理者79 Handler handlerA = new ConcreteHandlerA();80 Handler handlerB = new ConcreteHandlerB();81 Handler handlerC = new ConcreteHandlerC();82 83 // 构建责任链84 handlerA.setSuccessor(handlerB);85 handlerB.setSuccessor(handlerC);86 87 // 创建请求88 Request requestA = new Request("TypeA", "处理A类型请求", 5);89 Request requestB = new Request("TypeB", "处理B类型请求", 8);90 Request requestC = new Request("TypeC", "处理高优先级请求", 15);91 Request requestD = new Request("TypeD", "无人能处理的请求", 2);92 93 // 处理请求94 handlerA.handleRequest(requestA); // 由处理者A处理95 handlerA.handleRequest(requestB); // 由处理者B处理96 handlerA.handleRequest(requestC); // 由处理者C处理97 handlerA.handleRequest(requestD); // 无人处理98 }99}责任链模式变体
- 纯责任链
- 多级处理链
- 函数式责任链
纯的责任链模式中,一个请求只会被链中的一个处理者处理:
1// 纯责任链模式2public abstract class PureHandler {3 protected PureHandler successor;4 5 public void setSuccessor(PureHandler successor) {6 this.successor = successor;7 }8 9 public abstract boolean handleRequest(Request request);10}1112public class ConcreteHandlerX extends PureHandler {13 @Override14 public boolean handleRequest(Request request) {15 if ("TypeX".equals(request.getType())) {16 System.out.println("处理者X处理请求:" + request.getContent());17 return true; // 请求已处理,不再传递18 } else if (successor != null) {19 return successor.handleRequest(request);20 }21 return false; // 请求未被处理22 }23}2425// 使用示例26public static void main(String[] args) {27 PureHandler handlerX = new ConcreteHandlerX();28 PureHandler handlerY = new ConcreteHandlerY();29 30 handlerX.setSuccessor(handlerY);31 32 boolean handled = handlerX.handleRequest(new Request("TypeX", "请求内容", 1));33 System.out.println("请求是否被处理:" + handled);34}多级处理链允许一个请求被链中的多个处理者按顺序处理:
1// 多级处理链2public interface MultiHandler {3 boolean handle(Request request);4 void setNext(MultiHandler next);5}67public class BaseMultiHandler implements MultiHandler {8 private MultiHandler next;9 10 @Override11 public void setNext(MultiHandler next) {12 this.next = next;13 }14 15 @Override16 public boolean handle(Request request) {17 boolean handled = doHandle(request);18 19 // 无论是否处理成功,都传递给下一个处理者20 if (next != null) {21 return handled | next.handle(request);22 }23 24 return handled;25 }26 27 protected boolean doHandle(Request request) {28 // 由子类实现具体处理逻辑29 return false;30 }31}3233// 日志处理器34public class LoggingHandler extends BaseMultiHandler {35 @Override36 protected boolean doHandle(Request request) {37 System.out.println("日志记录请求:" + request.getContent());38 return true;39 }40}4142// 认证处理器43public class AuthHandler extends BaseMultiHandler {44 @Override45 protected boolean doHandle(Request request) {46 if (request.getContent().contains("authorized")) {47 System.out.println("认证通过");48 return true;49 }50 System.out.println("认证失败,终止处理");51 return false;52 }53}5455// 使用示例56public static void main(String[] args) {57 MultiHandler logger = new LoggingHandler();58 MultiHandler auth = new AuthHandler();59 MultiHandler processor = new RequestProcessor();60 61 logger.setNext(auth);62 auth.setNext(processor);63 64 Request request = new Request("API", "authorized api call", 1);65 logger.handle(request); // 请求会被所有三个处理者处理66}使用函数式接口实现责任链,使代码更加简洁:
1// 使用Function实现责任链2public class FunctionalChain {3 public static void main(String[] args) {4 // 使用函数组合创建责任链5 Function<Request, Request> chain = request -> {6 System.out.println("处理日志...");7 return request;8 }.andThen(request -> {9 if (request.getContent().contains("authorized")) {10 System.out.println("认证成功...");11 return request;12 } else {13 System.out.println("认证失败");14 return null;15 }16 }).andThen(request -> {17 if (request != null) {18 System.out.println("处理业务逻辑...");19 return request;20 }21 return null;22 });23 24 // 处理请求25 Request result = chain.apply(new Request("API", "authorized call", 1));26 System.out.println("请求处理结果:" + (result != null ? "成功" : "失败"));27 }28}2930// 使用Optional和Stream实现责任链31public class StreamChain {32 public static void main(String[] args) {33 List<Function<Request, Optional<Request>>> handlers = Arrays.asList(34 request -> {35 System.out.println("日志处理");36 return Optional.of(request);37 },38 request -> {39 if (request.getPriority() > 5) {40 System.out.println("优先级处理");41 return Optional.of(request);42 } else {43 return Optional.empty();44 }45 },46 request -> {47 System.out.println("最终处理");48 return Optional.of(request);49 }50 );51 52 Request initialRequest = new Request("Type", "Content", 10);53 54 Optional<Request> result = handlers.stream()55 .reduce(56 Optional.of(initialRequest),57 (req, handler) -> req.flatMap(handler),58 (a, b) -> a.isPresent() ? a : b59 );60 61 System.out.println("处理结果:" + result.isPresent());62 }63}责任链模式应用实例
- 过滤器链
- 审批流程
1// Web应用中的过滤器责任链2public interface Filter {3 void doFilter(Request request, Response response, FilterChain chain);4}56public class FilterChain {7 private List<Filter> filters = new ArrayList<>();8 private int index = 0;9 10 public FilterChain addFilter(Filter filter) {11 filters.add(filter);12 return this;13 }14 15 public void doFilter(Request request, Response response) {16 if (index < filters.size()) {17 Filter filter = filters.get(index);18 index++;19 filter.doFilter(request, response, this);20 }21 }22}2324// 安全过滤器25public class SecurityFilter implements Filter {26 @Override27 public void doFilter(Request request, Response response, FilterChain chain) {28 // 检查请求中的安全凭证29 String token = request.getParameter("token");30 if (token != null && validateToken(token)) {31 System.out.println("SecurityFilter: 安全验证通过");32 // 继续传递请求给下一个过滤器33 chain.doFilter(request, response);34 } else {35 System.out.println("SecurityFilter: 安全验证失败");36 response.setStatus(403); // Forbidden37 }38 }39 40 private boolean validateToken(String token) {41 return "valid_token".equals(token);42 }43}4445// 日志过滤器46public class LoggingFilter implements Filter {47 @Override48 public void doFilter(Request request, Response response, FilterChain chain) {49 System.out.println("LoggingFilter: 请求开始处理 - " + request.getUrl());50 51 // 继续传递请求给下一个过滤器52 chain.doFilter(request, response);53 54 System.out.println("LoggingFilter: 请求处理完成 - 状态: " + response.getStatus());55 }56}5758// 缓存过滤器59public class CacheFilter implements Filter {60 private Map<String, String> cache = new HashMap<>();61 62 @Override63 public void doFilter(Request request, Response response, FilterChain chain) {64 String url = request.getUrl();65 66 if (cache.containsKey(url)) {67 System.out.println("CacheFilter: 从缓存返回响应");68 response.setContent(cache.get(url));69 return; // 不再继续传递请求70 }71 72 System.out.println("CacheFilter: 未找到缓存,继续处理");73 chain.doFilter(request, response);74 75 // 缓存响应内容76 if (response.getStatus() == 200) {77 cache.put(url, response.getContent());78 System.out.println("CacheFilter: 响应已缓存");79 }80 }81}8283// 简化的请求和响应类84public class Request {85 private String url;86 private Map<String, String> parameters = new HashMap<>();87 88 public Request(String url) {89 this.url = url;90 }91 92 public void addParameter(String name, String value) {93 parameters.put(name, value);94 }95 96 public String getParameter(String name) {97 return parameters.get(name);98 }99 100 public String getUrl() {101 return url;102 }103}104105public class Response {106 private int status = 200;107 private String content = "";108 109 public void setStatus(int status) {110 this.status = status;111 }112 113 public int getStatus() {114 return status;115 }116 117 public void setContent(String content) {118 this.content = content;119 }120 121 public String getContent() {122 return content;123 }124}125126// 使用示例127public static void main(String[] args) {128 FilterChain chain = new FilterChain();129 chain.addFilter(new LoggingFilter())130 .addFilter(new SecurityFilter())131 .addFilter(new CacheFilter());132 133 // 创建一个带有有效token的请求134 Request request1 = new Request("http://example.com/api/data");135 request1.addParameter("token", "valid_token");136 Response response1 = new Response();137 138 // 处理请求139 chain.doFilter(request1, response1);140 141 // 重置过滤器链,再次处理相同的URL(将使用缓存)142 chain = new FilterChain();143 chain.addFilter(new LoggingFilter())144 .addFilter(new SecurityFilter())145 .addFilter(new CacheFilter());146 147 Request request2 = new Request("http://example.com/api/data");148 request2.addParameter("token", "valid_token");149 Response response2 = new Response();150 151 chain.doFilter(request2, response2);152}1// 企业审批流程中的责任链模式2public class ApprovalRequest {3 private String type;4 private double amount;5 private String description;6 7 public ApprovalRequest(String type, double amount, String description) {8 this.type = type;9 this.amount = amount;10 this.description = description;11 }12 13 public String getType() { return type; }14 public double getAmount() { return amount; }15 public String getDescription() { return description; }16}1718public abstract class Approver {19 protected Approver successor;20 protected String name;21 22 public Approver(String name) {23 this.name = name;24 }25 26 public void setSuccessor(Approver successor) {27 this.successor = successor;28 }29 30 public abstract void processRequest(ApprovalRequest request);31}3233// 部门经理 - 可以审批1000元以下的请求34public class DepartmentManager extends Approver {35 public DepartmentManager(String name) {36 super(name);37 }38 39 @Override40 public void processRequest(ApprovalRequest request) {41 if (request.getAmount() <= 1000) {42 System.out.println(String.format(43 "部门经理 %s 审批了 %.2f 元的 %s: %s", 44 name, request.getAmount(), request.getType(), request.getDescription()45 ));46 } else if (successor != null) {47 System.out.println(String.format(48 "部门经理 %s 无权审批,转交给上级", name49 ));50 successor.processRequest(request);51 } else {52 System.out.println("请求无法被审批");53 }54 }55}5657// 副总裁 - 可以审批10000元以下的请求58public class VicePresident extends Approver {59 public VicePresident(String name) {60 super(name);61 }62 63 @Override64 public void processRequest(ApprovalRequest request) {65 if (request.getAmount() <= 10000) {66 System.out.println(String.format(67 "副总裁 %s 审批了 %.2f 元的 %s: %s", 68 name, request.getAmount(), request.getType(), request.getDescription()69 ));70 } else if (successor != null) {71 System.out.println(String.format(72 "副总裁 %s 无权审批,转交给上级", name73 ));74 successor.processRequest(request);75 } else {76 System.out.println("请求无法被审批");77 }78 }79}8081// 总裁 - 可以审批50000元以下的请求82public class President extends Approver {83 public President(String name) {84 super(name);85 }86 87 @Override88 public void processRequest(ApprovalRequest request) {89 if (request.getAmount() <= 50000) {90 System.out.println(String.format(91 "总裁 %s 审批了 %.2f 元的 %s: %s", 92 name, request.getAmount(), request.getType(), request.getDescription()93 ));94 } else if (successor != null) {95 System.out.println(String.format(96 "总裁 %s 无权审批,转交给董事会", name97 ));98 successor.processRequest(request);99 } else {100 System.out.println("请求无法被审批");101 }102 }103}104105// 董事会 - 可以审批任何金额的请求106public class BoardOfDirectors extends Approver {107 public BoardOfDirectors(String name) {108 super(name);109 }110 111 @Override112 public void processRequest(ApprovalRequest request) {113 System.out.println(String.format(114 "董事会 %s 审批了 %.2f 元的 %s: %s", 115 name, request.getAmount(), request.getType(), request.getDescription()116 ));117 }118}119120// 使用示例121public static void main(String[] args) {122 // 创建审批人123 Approver manager = new DepartmentManager("张经理");124 Approver vp = new VicePresident("王副总");125 Approver president = new President("李总裁");126 Approver board = new BoardOfDirectors("董事会");127 128 // 构建责任链129 manager.setSuccessor(vp);130 vp.setSuccessor(president);131 president.setSuccessor(board);132 133 // 创建不同金额的报销请求134 ApprovalRequest request1 = new ApprovalRequest("差旅费", 800, "出差上海");135 ApprovalRequest request2 = new ApprovalRequest("设备采购", 8000, "购买笔记本电脑");136 ApprovalRequest request3 = new ApprovalRequest("项目投资", 30000, "市场推广活动");137 ApprovalRequest request4 = new ApprovalRequest("并购支出", 100000, "收购初创公司");138 139 // 处理请求140 manager.processRequest(request1); // 由部门经理审批141 manager.processRequest(request2); // 由副总裁审批142 manager.processRequest(request3); // 由总裁审批143 manager.processRequest(request4); // 由董事会审批144}责任链模式与其他模式比较
| 模式 | 区别 | 组合使用场景 |
|---|---|---|
| 责任链模式 vs 命令模式 | 责任链关注的是谁来处理请求,命令模式关注的是如何封装请求 | 可以在责任链中使用命令对象代表具体请求 |
| 责任链模式 vs 装饰器模式 | 责任链处理者可以选择是否将请求传递给后续处理者,装饰器总是将请求传递给被装饰对象 | 装饰器可以用于增强责任链的处理能力 |
| 责任链模式 vs 组合模式 | 责任链通常是线性结构,组合模式是树形结构 | 可以使用组合模式构建树形责任链 |
责任链模式实现要点
-
处理链的构建方式:
- 显式构建(手动设置后继者)
- 自动构建(使用依赖注入或配置文件)
-
传递机制选择:
- 纯责任链:一个请求只由一个处理者处理
- 多级处理链:一个请求可以被多个处理者处理
-
动态调整责任链:
java1// 动态调整责任链2public class DynamicChain {3 private List<Handler> handlers = new ArrayList<>();4 private Map<String, Integer> handlerPriority = new HashMap<>();56 public void addHandler(String name, Handler handler, int priority) {7 handlers.add(handler);8 handlerPriority.put(name, priority);910 // 按优先级排序11 handlers.sort((h1, h2) -> {12 String name1 = h1.getClass().getSimpleName();13 String name2 = h2.getClass().getSimpleName();14 return handlerPriority.get(name1) - handlerPriority.get(name2);15 });1617 // 重新构建链18 for (int i = 0; i < handlers.size() - 1; i++) {19 handlers.get(i).setSuccessor(handlers.get(i + 1));20 }21 }2223 public void removeHandler(String name) {24 handlers.removeIf(h -> h.getClass().getSimpleName().equals(name));25 handlerPriority.remove(name);2627 // 重新构建链28 for (int i = 0; i < handlers.size() - 1; i++) {29 handlers.get(i).setSuccessor(handlers.get(i + 1));30 }31 }3233 public void handleRequest(Request request) {34 if (!handlers.isEmpty()) {35 handlers.get(0).handleRequest(request);36 }37 }38} -
异常处理:
java1// 处理链中的异常处理2public class HandlerWithExceptionHandling extends Handler {3 @Override4 public void handleRequest(Request request) {5 try {6 // 尝试处理请求7 doHandle(request);8 } catch (Exception e) {9 // 处理异常10 handleException(request, e);11 } finally {12 // 无论是否异常,都继续传递给下一个处理者13 if (successor != null) {14 successor.handleRequest(request);15 }16 }17 }1819 protected void doHandle(Request request) throws Exception {20 // 具体处理逻辑21 }2223 protected void handleException(Request request, Exception e) {24 System.out.println("处理请求时发生异常: " + e.getMessage());25 // 记录异常日志或执行其他异常处理操作26 }27} -
返回处理结果:
java1// 带返回结果的处理链2public interface ResultHandler<T, R> {3 R handle(T request, HandlerChain<T, R> chain);4}56public class HandlerChain<T, R> {7 private List<ResultHandler<T, R>> handlers = new ArrayList<>();8 private int index = 0;9 private R defaultResult;1011 public HandlerChain(R defaultResult) {12 this.defaultResult = defaultResult;13 }1415 public void addHandler(ResultHandler<T, R> handler) {16 handlers.add(handler);17 }1819 public R handle(T request) {20 if (index < handlers.size()) {21 ResultHandler<T, R> handler = handlers.get(index++);22 return handler.handle(request, this);23 }24 return defaultResult;25 }26}
- 明确每个处理者的职责范围和处理条件
- 避免责任链过长,导致请求处理延迟
- 为无法处理的请求提供默认处理机制
- 考虑使用返回值表示处理状态,方便调试和追踪
- 责任链适合流程处理,可以和状态模式结合实现复杂流程
- 使用责任链代替复杂的if-else或switch语句
- 结合命令模式可以实现可撤销的责任链
评论