1. 行为型模式概述
1.1 什么是行为型模式?
行为型模式是GoF(Gang of Four)在《设计模式:可复用面向对象软件的基础》中定义的一类设计模式,专注于对象之间的职责分配和通信机制。它们通过定义对象交互的方式,解决系统在运行时的动态行为问题,确保代码的灵活性、可扩展性和可维护性。
与结构型模式(如代理模式、享元模式)关注对象组合不同,行为型模式更关注对象如何协作完成任务。例如,观察者模式定义了一对多的依赖关系,策略模式允许动态切换算法,责任链模式将请求沿链传递。这些模式通过松耦合的方式优化对象交互,符合单一职责原则(SRP)和开闭原则(OCP)。
在Java中,行为型模式广泛应用于框架和库设计。例如,Spring的事件机制使用观察者模式,Servlet过滤器使用责任链模式,Java 8的Stream API与策略模式高度契合。行为型模式的本质是职责分离与动态协作,通过接口和抽象类实现灵活的交互逻辑。
1.2 行为型模式的分类与核心组成
GoF定义了11种行为型模式,以下是主要模式及其核心思想:
- 观察者模式(Observer Pattern):定义对象间一对多的依赖,主体变化时通知所有依赖者。
- 策略模式(Strategy Pattern):封装可互换的算法,允许运行时动态选择。
- 责任链模式(Chain of Responsibility Pattern):将请求沿处理链传递,直到被处理。
- 命令模式(Command Pattern):将请求封装为对象,支持撤销、重做等操作。
- 状态模式(State Pattern):根据对象状态改变行为。
- 模板方法模式(Template Method Pattern):定义算法骨架,子类实现具体步骤。
- 访问者模式(Visitor Pattern):分离数据结构与操作,适合稳定的数据结构。
- 中介者模式(Mediator Pattern):通过中介对象协调多个对象交互。
- 迭代器模式(Iterator Pattern):提供顺序访问集合元素的方式。
- 备忘录模式(Memento Pattern):保存和恢复对象状态。
- 解释器模式(Interpreter Pattern):为语言定义语法表示和解释器。
每种模式解决特定场景的协作问题,核心组成包括:
- 抽象接口:定义交互契约(如观察者的
update方法)。 - 具体实现:实现具体行为逻辑。
- 客户端:协调对象交互,触发行为。
2. 行为型模式的原理与结构
2.1 行为型模式的通用原理
行为型模式的核心在于对象协作与职责分配。它们通过以下机制实现:
- 松耦合:通过接口或抽象类解耦调用者与实现者。
- 动态性:支持运行时行为切换(如策略模式)。
- 职责分离:将复杂逻辑分解为单一职责的组件。
- 通信优化:减少对象间直接依赖,降低耦合度。
例如,观察者模式通过事件订阅机制实现一对多通知,策略模式通过算法封装实现行为可替换。这些模式通常依赖多态、继承或组合来实现灵活性。
2.2 行为型模式的UML结构
由于行为型模式种类较多,以下以观察者模式为例展示典型UML结构:
+------------------+ +------------------+
| Subject | | Observer |
+------------------+ +------------------+
| + attach(Observer)|<----->| + update() |
| + detach(Observer)| +------------------+
| + notify() | ^
+------------------+ |
+------------------+
| ConcreteObserver |
+------------------+
| - state |
| + update() |
+------------------+- Subject:管理观察者列表,触发通知。
- Observer:定义更新接口。
- ConcreteObserver:实现具体更新逻辑。
- Client:创建主体和观察者,协调交互。
其他模式(如策略模式、责任链模式)有类似结构,核心是接口驱动的协作。
3. 行为型模式的Java实现
为深入理解行为型模式,以下通过三种典型模式(观察者模式、策略模式、责任链模式)的详细实现展示其应用。
3.1 观察者模式:新闻订阅系统
观察者模式适合一对多的依赖场景,如新闻订阅系统,当新闻发布时,所有订阅者收到通知。
3.1.1 主体和观察者接口
// 观察者接口
interface NewsSubscriber {
void update(String news);
}
// 主体接口
interface NewsAgency {
void addSubscriber(NewsSubscriber subscriber);
void removeSubscriber(NewsSubscriber subscriber);
void publishNews(String news);
}3.1.2 具体主体和观察者
import java.util.ArrayList;
import java.util.List;
// 具体主体:新闻机构
class ConcreteNewsAgency implements NewsAgency {
private List<NewsSubscriber> subscribers = new ArrayList<>();
private String latestNews;
@Override
public void addSubscriber(NewsSubscriber subscriber) {
subscribers.add(subscriber);
}
@Override
public void removeSubscriber(NewsSubscriber subscriber) {
subscribers.remove(subscriber);
}
@Override
public void publishNews(String news) {
this.latestNews = news;
notifySubscribers();
}
private void notifySubscribers() {
for (NewsSubscriber subscriber : subscribers) {
subscriber.update(latestNews);
}
}
}
// 具体观察者:用户
class UserSubscriber implements NewsSubscriber {
private String name;
public UserSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println(name + " 收到新闻:" + news);
}
}3.1.3 客户端代码
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteNewsAgency agency = new ConcreteNewsAgency();
NewsSubscriber user1 = new UserSubscriber("张三");
NewsSubscriber user2 = new UserSubscriber("李四");
agency.addSubscriber(user1);
agency.addSubscriber(user2);
agency.publishNews("重大新闻:Java 21发布!");
agency.removeSubscriber(user1);
agency.publishNews("后续报道:新特性解析");
}
}运行结果:
张三 收到新闻:重大新闻:Java 21发布!
李四 收到新闻:重大新闻:Java 21发布!
李四 收到新闻:后续报道:新特性解析分析:观察者模式通过解耦主体与观察者,实现动态订阅和通知。Java标准库中的java.util.Observable和Observer(已废弃,推荐事件监听机制)是其典型实现。
3.2 策略模式:支付系统
策略模式允许动态切换算法,适合需要灵活替换行为的场景,如支付系统中支持多种支付方式。
3.2.1 策略接口和具体策略
// 策略接口
interface PaymentStrategy {
void pay(double amount);
}
// 具体策略:支付宝支付
class AlipayStrategy implements PaymentStrategy {
private String account;
public AlipayStrategy(String account) {
this.account = account;
}
@Override
public void pay(double amount) {
System.out.println("通过支付宝账户 " + account + " 支付 " + amount + " 元");
}
}
// 具体策略:微信支付
class WechatPayStrategy implements PaymentStrategy {
private String openId;
public WechatPayStrategy(String openId) {
this.openId = openId;
}
@Override
public void pay(double amount) {
System.out.println("通过微信 openId " + openId + " 支付 " + amount + " 元");
}
}3.2.2 上下文类
// 上下文:支付服务
class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(double amount) {
if (strategy == null) {
throw new IllegalStateException("支付策略未设置");
}
strategy.pay(amount);
}
}3.2.3 客户端代码
public class StrategyPatternDemo {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
// 使用支付宝支付
context.setStrategy(new AlipayStrategy("user@example.com"));
context.executePayment(100.50);
// 切换到微信支付
context.setStrategy(new WechatPayStrategy("wx123456"));
context.executePayment(200.75);
}
}运行结果:
通过支付宝账户 user@example.com 支付 100.5 元
通过微信 openId wx123456 支付 200.75 元分析:策略模式通过接口封装算法,允许运行时动态切换支付方式,符合开闭原则。Java 8的函数式接口(如Function)可进一步简化策略实现。
3.3 责任链模式:日志处理系统
责任链模式将请求沿处理链传递,直到被处理,适合分级处理场景,如日志系统。
3.3.1 处理者接口和抽象类
// 处理者接口
interface Logger {
void log(String message, int level);
}
// 抽象处理者
abstract class AbstractLogger implements Logger {
protected Logger nextLogger;
protected int level;
public void setNextLogger(Logger nextLogger) {
this.nextLogger = nextLogger;
}
@Override
public void log(String message, int level) {
if (this.level <= level) {
write(message);
}
if (nextLogger != null) {
nextLogger.log(message, level);
}
}
protected abstract void write(String message);
}3.3.2 具体处理者
// 具体处理者:控制台日志
class ConsoleLogger extends AbstractLogger {
public ConsoleLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("控制台日志:" + message);
}
}
// 具体处理者:文件日志
class FileLogger extends AbstractLogger {
public FileLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("文件日志:" + message);
}
}3.3.3 客户端代码
public class ChainOfResponsibilityDemo {
public static final int INFO = 1;
public static final int DEBUG = 2;
public static final int ERROR = 3;
public static void main(String[] args) {
Logger consoleLogger = new ConsoleLogger(INFO);
Logger fileLogger = new FileLogger(ERROR);
consoleLogger.setNextLogger(fileLogger);
consoleLogger.log("信息日志", INFO);
System.out.println("-----------------");
consoleLogger.log("错误日志", ERROR);
}
}运行结果:
控制台日志:信息日志
-----------------
控制台日志:错误日志
文件日志:错误日志分析:责任链模式通过链式传递处理请求,适合分级日志、过滤器等场景。Java Servlet的FilterChain是其典型应用。
4. 行为型模式的应用场景
4.1 事件驱动系统
观察者模式广泛用于:
- GUI事件监听(如Swing的ActionListener)。
- Spring的事件机制(
ApplicationEvent)。 - 消息队列(如Kafka消费者)。
4.2 算法动态选择
策略模式适用于:
- 支付系统(如3.2示例)。
- 排序算法选择(Java的
Collections.sort)。 - 游戏AI行为切换。
4.3 请求分级处理
责任链模式适合:
- 日志系统(如3.3示例)。
- Servlet过滤器。
- 审批流程(如请假审批)。
4.4 其他模式场景
- 命令模式:支持撤销操作(如文本编辑器)。
- 状态模式:状态机实现(如订单状态流转)。
- 模板方法模式:框架算法骨架(如Spring的
JdbcTemplate)。
5. 行为型模式的优缺点
5.1 行为型模式的优点
- 灵活性:动态行为切换,适应运行时变化。
- 松耦合:通过接口解耦调用者与实现者。
- 可扩展性:易于添加新行为或处理者。
- 职责清晰:单一职责分配提升代码可读性。
5.2 行为型模式的缺点
- 复杂性增加:多对象交互可能导致设计复杂。
- 性能开销:动态调用或链式传递可能影响性能。
- 调试困难:事件流或职责链难以跟踪。
- 过度使用:简单场景使用复杂模式增加维护成本。
6. 行为型模式与其他模式的关系
6.1 行为型模式 vs. 结构型模式
- 行为型模式:关注对象交互与职责分配。
- 结构型模式(如历史对话中的代理、享元模式):关注对象组合与结构优化。
例如,代理模式控制访问,观察者模式管理通知,两者可结合(如代理拦截通知)。
6.2 行为型模式内部对比
- 观察者 vs. 中介者:观察者适合一对多通知,中介者适合多对多协调。
- 策略 vs. 状态:策略关注算法选择,状态关注状态驱动行为。
6.3 与创建型模式结合
行为型模式常与工厂模式结合,动态创建策略或观察者。
7. 行为型模式在实际项目中的案例分析
7.1 Spring中的观察者模式
Spring的ApplicationEventPublisher实现观察者模式:
@Component
public class UserEventPublisher {
@Autowired
private ApplicationEventPublisher publisher;
public void publishEvent(String user) {
publisher.publishEvent(new UserEvent(this, user));
}
}
class UserEvent extends ApplicationEvent {
private String user;
public UserEvent(Object source, String user) {
super(source);
this.user = user;
}
}
@Component
class UserEventListener implements ApplicationListener<UserEvent> {
@Override
public void onApplicationEvent(UserEvent event) {
System.out.println("用户事件:" + event.getUser());
}
}7.2 Servlet过滤器中的责任链模式
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
System.out.println("认证过滤器");
chain.doFilter(request, response); // 传递到下一过滤器
}
}7.3 策略模式在Java Stream API
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5);
numbers.stream()
.sorted(Comparator.comparingInt(a -> a)) // 策略:升序
.forEach(System.out::println);Comparator作为策略接口,动态定义排序行为。
8. 行为型模式的注意事项与最佳实践
8.1 设计注意事项
- 接口简洁:行为接口应专注核心职责。
- 线程安全:观察者模式需考虑并发通知问题。
- 职责清晰:避免责任链过长或策略过多。
8.2 性能优化
- 缓存策略:缓存常用策略对象。
- 异步通知:观察者模式结合事件队列提升性能。
- 链式优化:责任链避免不必要的传递。
8.3 常见坑点
- 循环通知:观察者模式可能导致循环调用。
- 策略膨胀:过多策略增加维护成本。
- 链路复杂:责任链过长导致调试困难。
