设计模式之行为型模式
关注对象之间的职责分配、通信和协作,描述对象如何交互以及如何分配职责。
1. 责任链模式
1.1 为什么需要?
当需要多个对象按顺序处理同一个请求时(如审批流程、过滤器链),避免请求发送者与多个处理者耦合。责任链模式将处理者连成链条,请求沿链传递直到被处理。
1.2 核心原理
处理器抽象:定义处理请求的接口和设置下一个处理者的方法
具体处理器:实现处理逻辑,若无法处理则转发给下一个处理器
链式传递:处理器对象形成链条,请求按顺序传递
1.3 最佳实践场景
多级审批系统(请假审批)
Web请求过滤器(Servlet Filter)
日志处理管道
异常处理链路
1.4 java代码示例
// 1. 抽象处理器
abstract class Handler {
protected Handler next;
public void setNext(Handler next) { this.next = next; }
public abstract void handleRequest(int request);
}
// 2. 具体处理器
class Manager extends Handler {
@Override
public void handleRequest(int amount) {
if (amount <= 1000) {
System.out.println("经理审批通过: " + amount);
} else if (next != null) {
next.handleRequest(amount); // 转交上级
}
}
}
class Director extends Handler {
@Override
public void handleRequest(int amount) {
if (amount <= 5000) {
System.out.println("总监审批通过: " + amount);
} else if (next != null) {
next.handleRequest(amount);
}
}
}
// 3. 客户端使用
public class Client {
public static void main(String[] args) {
Handler manager = new Manager();
Handler director = new Director();
manager.setNext(director); // 形成责任链
// 处理请求
manager.handleRequest(800); // 经理处理
manager.handleRequest(4500); // 总监处理
}
}
2. 命令模式
2.1 为什么需要?
将请求封装为独立对象,支持请求排队、记录日志、撤销/重做等操作。解耦请求发起者与执行者,使系统更易扩展。
2.2 核心原理
命令接口:声明执行方法(如
execute()
)具体命令:实现命令接口,绑定接收者与操作
调用者:触发命令的执行
接收者:实际执行操作的对象
2.3 最佳实践场景
GUI按钮/菜单操作
事务管理(支持回滚)
任务队列系统
游戏操作控制
2.4 java代码示例
// 1. 命令接口
interface Command {
void execute();
}
// 2. 接收者(实际执行者)
class Light {
public void turnOn() { System.out.println("开灯"); }
public void turnOff() { System.out.println("关灯"); }
}
// 3. 具体命令
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
@Override
public void execute() { light.turnOn(); }
}
// 4. 调用者(触发命令)
class RemoteControl {
private Command command;
public void setCommand(Command command) { this.command = command; }
public void pressButton() { command.execute(); }
}
// 5. 客户端
public class Client {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton(); // 输出"开灯"
}
}
3. 解释器模式
3.1 为什么需要?
为特定领域问题(如SQL解析、正则表达式)定义语法规则,并解释执行语法规则。将复杂语法拆分为可管理的类结构。
3.2 核心原理
抽象表达式:声明解释操作的接口
终结符表达式:处理语法中的基本元素(如变量、常量)
非终结符表达式:组合表达式实现复杂逻辑(如运算、条件)
上下文环境:存储全局信息供表达式访问
3.3 最佳实践场景
数学公式解析器
SQL语句解析
编译器实现
规则引擎(如风控规则)
3.4 java代码示例(实现 a + b 的解析)
// 1. 抽象表达式
interface Expression {
int interpret(Context context);
}
// 2. 终结符表达式(变量)
class Variable implements Expression {
private String name;
public Variable(String name) { this.name = name; }
@Override
public int interpret(Context context) {
return context.get(name);
}
}
// 3. 非终结符表达式(加法)
class Add implements Expression {
private Expression left, right;
public Add(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
// 4. 上下文(存储变量值)
class Context {
private Map<String, Integer> map = new HashMap<>();
public void assign(String var, int value) { map.put(var, value); }
public int get(String var) { return map.get(var); }
}
// 5. 客户端
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.assign("a", 10);
context.assign("b", 5);
Expression expr = new Add(new Variable("a"), new Variable("b"));
System.out.println("结果: " + expr.interpret(context)); // 输出15
}
}
4. 迭代器模式
4.1 为什么需要?
提供统一的方式遍历集合对象(如列表、树),无需暴露集合内部结构。支持多种遍历方式(正序/倒序),符合单一职责原则。
4.2 核心原理
迭代器接口:定义
hasNext()
和next()
方法具体迭代器:实现集合的遍历逻辑
集合接口:声明创建迭代器的方法
具体集合:实现集合结构并返回迭代器
4.3 最佳实践场景
集合框架(Java的
Iterator
)树形结构遍历(深度优先/广度优先)
数据库查询结果集遍历
文件目录遍历
4.4 java代码示例(自定义集合迭代)
// 1. 迭代器接口
interface Iterator<T> {
boolean hasNext();
T next();
}
// 2. 集合接口
interface Container<T> {
Iterator<T> createIterator();
}
// 3. 具体集合
class NameRepository implements Container<String> {
private String[] names = {"Alice", "Bob", "Charlie"};
@Override
public Iterator<String> createIterator() {
return new NameIterator();
}
// 4. 具体迭代器(内部类)
private class NameIterator implements Iterator<String> {
int index = 0;
@Override
public boolean hasNext() { return index < names.length; }
@Override
public String next() { return names[index++]; }
}
}
// 5. 客户端
public class Client {
public static void main(String[] args) {
NameRepository repository = new NameRepository();
Iterator<String> iter = repository.createIterator();
while (iter.hasNext()) {
System.out.println("Name: " + iter.next());
}
/* 输出:
Name: Alice
Name: Bob
Name: Charlie */
}
}
5. 中介者模式
5.1 为什么需要
当多个对象存在复杂网状引用关系时(如聊天室中的用户互相调用),直接通信会导致耦合度高且难以维护。中介者模式通过引入中介对象来封装交互逻辑,将多对多关系转为一对多关系。
5.2 核心原理
定义一个中介接口封装对象间的交互,所有对象通过中介者通信,而非直接引用彼此。核心组件:
Mediator
(抽象中介者):定义通信接口ConcreteMediator
(具体中介者):实现协调逻辑Colleague
(同事类):持有中介者引用
5.3 最佳实践场景
聊天室系统(用户间消息转发)
飞机调度系统(塔台协调航班)
GUI组件交互(按钮/输入框联动)
5.4 java代码示例
// 1. 中介者接口
interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}
// 2. 具体中介者
class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
public void addUser(User user) {
users.add(user);
}
public void sendMessage(String msg, User sender) {
for (User u : users) {
if (u != sender) u.receive(msg); // 中介者转发消息
}
}
}
// 3. 同事类
abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator med, String name) {
this.mediator = med;
this.name = name;
}
public abstract void send(String msg);
public abstract void receive(String msg);
}
// 4. 具体同事类
class ChatUser extends User {
public ChatUser(ChatMediator med, String name) {
super(med, name);
}
public void send(String msg) {
System.out.println(name + "发送: " + msg);
mediator.sendMessage(msg, this); // 通过中介者发送
}
public void receive(String msg) {
System.out.println(name + "收到: " + msg);
}
}
// 使用
public class Main {
public static void main(String[] args) {
ChatMediator mediator = new ChatRoom();
User user1 = new ChatUser(mediator, "Alice");
User user2 = new ChatUser(mediator, "Bob");
mediator.addUser(user1);
mediator.addUser(user2);
user1.send("你好!"); // 输出: Alice发送: 你好! Bob收到: 你好!
}
}
6. 备忘录模式
6.1 为什么需要?
当需要保存对象状态以便后续恢复(如撤销操作、游戏存档)时,直接暴露对象内部状态会破坏封装性。备忘录模式提供状态快照管理机制,不破坏原始对象的封装。
6.2 核心原理
将状态保存在独立对象(备忘录)中,由原发器创建/恢复状态,管理者负责存储备忘录。核心组件:
Originator
(原发器):创建/恢复备忘录Memento
(备忘录):存储状态快照Caretaker
(管理者):存储和管理备忘录
6.3 最佳实践场景
文本编辑器的撤销/重做
游戏进度保存
数据库事务回滚
6.4 java代码示例
// 1. 备忘录(存储状态)
class EditorMemento {
private final String content;
public EditorMemento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
// 2. 原发器(创建/恢复备忘录)
class TextEditor {
private String content = "";
public void type(String text) {
content += text;
}
public EditorMemento save() {
return new EditorMemento(content); // 创建快照
}
public void restore(EditorMemento m) {
content = m.getContent(); // 恢复状态
}
public void print() {
System.out.println("当前内容: " + content);
}
}
// 3. 管理者
class History {
private Stack<EditorMemento> states = new Stack<>();
public void push(EditorMemento m) {
states.push(m);
}
public EditorMemento pop() {
return states.pop();
}
}
// 使用
public class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
History history = new History();
editor.type("Hello");
history.push(editor.save()); // 保存状态1
editor.type(" World!");
history.push(editor.save()); // 保存状态2
editor.restore(history.pop()); // 撤销到状态1
editor.print(); // 输出: 当前内容: Hello
}
}
7. 观察者模式
7.1 为什么需要?
当对象状态变化需通知其他对象时(如新闻订阅),直接调用依赖对象会导致紧耦合。观察者模式建立发布-订阅机制,实现松耦合的状态同步。
7.2 核心原理
主题(Subject)维护观察者列表,状态变化时自动通知所有观察者。核心组件:
Subject
:注册/通知观察者Observer
:定义更新接口ConcreteSubject
:存储状态并触发通知ConcreteObserver
:实现更新逻辑
7.3 最佳实践场景
事件驱动系统(按钮点击事件)
实时数据监控(股票价格更新)
消息队列消费者
7.4 java代码示例
// 1. 观察者接口
interface NewsSubscriber {
void update(String news);
}
// 2. 主题接口
interface NewsPublisher {
void subscribe(NewsSubscriber sub);
void unsubscribe(NewsSubscriber sub);
void notifySubscribers();
}
// 3. 具体主题
class CNN implements NewsPublisher {
private List<NewsSubscriber> subscribers = new ArrayList<>();
private String latestNews;
public void setNews(String news) {
this.latestNews = news;
notifySubscribers(); // 自动通知观察者
}
@Override
public void subscribe(NewsSubscriber sub) {
subscribers.add(sub);
}
@Override
public void unsubscribe(NewsSubscriber sub) {
subscribers.remove(sub);
}
@Override
public void notifySubscribers() {
for (NewsSubscriber sub : subscribers) {
sub.update(latestNews);
}
}
}
// 4. 具体观察者
class User implements NewsSubscriber {
private String name;
public User(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println(name + "收到新闻: " + news);
}
}
// 使用
public class Main {
public static void main(String[] args) {
CNN cnn = new CNN();
User alice = new User("Alice");
User bob = new User("Bob");
cnn.subscribe(alice);
cnn.subscribe(bob);
cnn.setNews("Java 21发布!");
// 输出: Alice收到新闻: Java 21发布!
// Bob收到新闻: Java 21发布!
}
}
8. 状态模式
8.1 为什么需要?
当对象行为随内部状态改变而改变(如订单状态流转),直接在代码中使用if-else判断状态会导致逻辑臃肿。状态模式将状态抽象为独立类,实现状态驱动的行为变化。
8.2 核心原理
将状态转移逻辑分散到不同状态类中,上下文(Context)委托当前状态对象执行行为。核心组件:
State
:抽象状态接口ConcreteState
:具体状态实现类Context
:持有当前状态,触发状态行为
8.3 最佳实践场景
订单状态管理(待支付/已发货/已完成)
电梯运行状态(停止/运行/故障)
游戏角色状态(正常/中毒/眩晕)
8.4 java代码示例
// 1. 状态接口
interface OrderState {
void next(Order order);
void prev(Order order);
}
// 2. 具体状态类
class PaymentPendingState implements OrderState {
@Override
public void next(Order order) {
order.setState(new ShippedState()); // 状态流转
System.out.println("订单已发货");
}
@Override
public void prev(Order order) {
System.out.println("初始状态,无上一状态");
}
}
class ShippedState implements OrderState {
@Override
public void next(Order order) {
order.setState(new DeliveredState());
System.out.println("订单已送达");
}
@Override
public void prev(Order order) {
order.setState(new PaymentPendingState());
System.out.println("退回待支付状态");
}
}
// 3. 上下文类
class Order {
private OrderState state;
public Order() {
state = new PaymentPendingState(); // 初始状态
}
public void setState(OrderState state) {
this.state = state;
}
public void nextState() {
state.next(this); // 委托状态对象处理
}
public void prevState() {
state.prev(this);
}
}
// 使用
public class Main {
public static void main(String[] args) {
Order order = new Order();
order.nextState(); // 输出: 订单已发货
order.nextState(); // 输出: 订单已送达
order.prevState(); // 输出: 退回待支付状态
}
}
9. 策略模式
9.1 为什么需要?
当系统需要在运行时动态选择算法行为时,避免使用大量条件语句(如if-else或switch)。策略模式将算法封装成独立对象,使它们可以互相替换,符合开闭原则。
9.2 核心原理
抽象策略接口:定义算法的公共接口
具体策略类:实现接口的具体算法
**环境类(Context)**:持有策略引用,负责调用策略
9.3 最佳实践场景
支付方式选择(微信/支付宝/银行卡)
数据压缩算法切换(ZIP/RAR)
导航路径计算(驾车/步行/公交)
9.4 Java代码示例
// 1. 策略接口
interface PaymentStrategy {
void pay(int amount);
}
// 2. 具体策略实现
class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("微信支付:" + amount + "元");
}
}
// 3. 环境类
class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(int amount) {
strategy.pay(amount);
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
// 动态切换策略
context.setStrategy(new AlipayStrategy());
context.executePayment(100);
context.setStrategy(new WechatPayStrategy());
context.executePayment(200);
}
}
10. 模板方法模式
10.1 为什么需要?
当多个类有相似的流程框架,但某些步骤实现不同时。模板方法模式定义算法骨架,将可变步骤延迟到子类实现,避免代码重复。
10.2 核心原理
抽象模板类:定义算法骨架(final模板方法)+ 抽象步骤
具体子类:实现抽象步骤,但不改变算法结构
10.3 最佳实践场景
数据库操作流程(连接→执行→关闭)
游戏初始化流程(加载资源→创建场景→启动循环)
文档导出流程(准备数据→生成文件→压缩)
10.4 Java代码示例
// 1. 抽象模板类
abstract class DataExporter {
// 模板方法(final防止子类重写)
public final void export() {
prepareData();
generateFile();
compressFile();
}
// 抽象步骤(子类实现)
protected abstract void prepareData();
protected abstract void generateFile();
// 默认实现(可被子类覆盖)
protected void compressFile() {
System.out.println("默认压缩为ZIP格式");
}
}
// 2. 具体实现类
class PDFExporter extends DataExporter {
@Override
protected void prepareData() {
System.out.println("准备PDF数据...");
}
@Override
protected void generateFile() {
System.out.println("生成PDF文件");
}
}
class ExcelExporter extends DataExporter {
@Override
protected void prepareData() {
System.out.println("准备Excel数据...");
}
@Override
protected void generateFile() {
System.out.println("生成XLSX文件");
}
@Override
protected void compressFile() {
System.out.println("自定义压缩为RAR格式");
}
}
// 3. 客户端使用
public class Client {
public static void main(String[] args) {
DataExporter exporter = new PDFExporter();
exporter.export(); // 执行完整流程
System.out.println("------");
exporter = new ExcelExporter();
exporter.export();
}
}
11. 访问者模式
11.1 为什么需要?
当需要对复杂对象结构(如组合结构)添加新操作,但不想修改元素类时。访问者模式将操作与元素结构分离,符合单一职责原则。
11.2 核心原理
访问者接口:声明访问操作
具体访问者:实现接口的操作
元素接口:定义accept(Visitor)方法
具体元素:实现accept方法,调用访问者
11.3 最佳实践场景
编译器语法树分析(类型检查/代码优化)
文件系统遍历(计算大小/搜索文件)
UI组件处理(渲染/校验)
11.4 Java代码示例
import java.util.ArrayList;
import java.util.List;
// 1. 元素接口
interface ComputerPart {
void accept(ComputerPartVisitor visitor);
}
// 2. 具体元素
class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor visitor) {
visitor.visit(this);
}
}
class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor visitor) {
visitor.visit(this);
}
}
// 3. 访问者接口
interface ComputerPartVisitor {
void visit(Mouse mouse);
void visit(Keyboard keyboard);
}
// 4. 具体访问者
class DisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Mouse mouse) {
System.out.println("显示鼠标");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("显示键盘");
}
}
class DiagnoseVisitor implements ComputerPartVisitor {
@Override
public void visit(Mouse mouse) {
System.out.println("诊断鼠标...状态正常");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("诊断键盘...按键检测通过");
}
}
// 5. 对象结构
class Computer implements ComputerPart {
private List<ComputerPart> parts = new ArrayList<>();
public Computer() {
parts.add(new Mouse());
parts.add(new Keyboard());
}
@Override
public void accept(ComputerPartVisitor visitor) {
for (ComputerPart part : parts) {
part.accept(visitor);
}
}
}
// 6. 客户端使用
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
// 应用不同访问者
computer.accept(new DisplayVisitor());
System.out.println("------");
computer.accept(new DiagnoseVisitor());
}
}