设计模式之行为型模式

发布于:2025-07-26 ⋅ 阅读:(20) ⋅ 点赞:(0)

设计模式之行为型模式

关注对象之间的职责分配、通信和协作,描述对象如何交互以及如何分配职责。

1. 责任链模式

1.1 为什么需要?

当需要多个对象按顺序处理同一个请求时(如审批流程、过滤器链),避免请求发送者与多个处理者耦合。责任链模式将处理者连成链条,请求沿链传递直到被处理。

1.2 核心原理
  1. 处理器抽象:定义处理请求的接口和设置下一个处理者的方法

  2. 具体处理器:实现处理逻辑,若无法处理则转发给下一个处理器

  3. 链式传递:处理器对象形成链条,请求按顺序传递

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 核心原理
  1. 命令接口:声明执行方法(如 execute()

  2. 具体命令:实现命令接口,绑定接收者与操作

  3. 调用者:触发命令的执行

  4. 接收者:实际执行操作的对象

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 核心原理
  1. 抽象表达式:声明解释操作的接口

  2. 终结符表达式:处理语法中的基本元素(如变量、常量)

  3. 非终结符表达式:组合表达式实现复杂逻辑(如运算、条件)

  4. 上下文环境:存储全局信息供表达式访问

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 核心原理
  1. 迭代器接口:定义 hasNext()next() 方法

  2. 具体迭代器:实现集合的遍历逻辑

  3. 集合接口:声明创建迭代器的方法

  4. 具体集合:实现集合结构并返回迭代器

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 核心原理

定义一个中介接口封装对象间的交互,所有对象通过中介者通信,而非直接引用彼此。核心组件:

  1. Mediator(抽象中介者):定义通信接口

  2. ConcreteMediator(具体中介者):实现协调逻辑

  3. 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 核心原理

将状态保存在独立对象(备忘录)中,由原发器创建/恢复状态,管理者负责存储备忘录。核心组件:

  1. Originator(原发器):创建/恢复备忘录

  2. Memento(备忘录):存储状态快照

  3. 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)维护观察者列表,状态变化时自动通知所有观察者。核心组件:

  1. Subject:注册/通知观察者

  2. Observer:定义更新接口

  3. ConcreteSubject:存储状态并触发通知

  4. 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)委托当前状态对象执行行为。核心组件:

  1. State:抽象状态接口

  2. ConcreteState:具体状态实现类

  3. 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 核心原理
  1. 抽象策略接口:定义算法的公共接口

  2. 具体策略类:实现接口的具体算法

  3. **环境类(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 核心原理
  1. 抽象模板类:定义算法骨架(final模板方法)+ 抽象步骤

  2. 具体子类:实现抽象步骤,但不改变算法结构

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 核心原理
  1. 访问者接口:声明访问操作

  2. 具体访问者:实现接口的操作

  3. 元素接口:定义accept(Visitor)方法

  4. 具体元素:实现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());
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到