设计模式专栏(四):行为型设计模式(Behavioral Design Patterns)

106 阅读11分钟

设计模式专栏(四):行为型设计模式(Behavioral Design Patterns)(关注“Beyond Code 程序员”订阅号查看更多文章)

模式介绍

行为型设计模式关注的是对象之间的通信与职责分配,它们用于定义对象之间的交互方式,确保系统中对象能够协作完成复杂的功能。 与创建型、结构型不同,行为型模式更侧重于算法的封装对象职责的解耦,从而提高系统的灵活性与可维护性。

行为型模式可以帮助我们:

  • 明确对象之间的职责划分
  • 降低对象之间的耦合度
  • 提高系统的可复用性和可扩展性
  • 通过封装算法或流程,减少代码重复

主要包含十一种模式(常见经典行为型模式)

  1. 责任链模式(Chain of Responsibility Pattern)
  2. 命令模式(Command Pattern)
  3. 解释器模式(Interpreter Pattern)
  4. 迭代器模式(Iterator Pattern)
  5. 中介者模式(Mediator Pattern)
  6. 备忘录模式(Memento Pattern)
  7. 观察者模式(Observer Pattern)
  8. 状态模式(State Pattern)
  9. 策略模式(Strategy Pattern)
  10. 模板方法模式(Template Method Pattern)
  11. 访问者模式(Visitor Pattern)

⚠️ 提示:设计模式是一种编程思想,其实现方式并非唯一。本文示例代码仅作为参考,并不意味着设计模式必须按照示例中的方式实现。

1. 责任链模式(Chain of Responsibility Pattern)

模式解释

责任链模式核心思想是:

将请求沿着处理链传递,链上的每个对象都有机会处理该请求,直到有对象处理它为止。

适用场景:

  • 多个对象可能处理同一个请求
  • 希望避免请求发送者与接收者之间的耦合
  • 处理逻辑需要动态组合或调整顺序

代码示例(Java,含测试代码)

// 抽象处理者
abstract class Handler {
    protected Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public abstract void handleRequest(int level);
}

// 具体处理者A
class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(int level) {
        if (level == 1) {
            System.out.println("HandlerA 处理了请求");
        } else if (next != null) {
            next.handleRequest(level);
        }
    }
}

// 具体处理者B
class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(int level) {
        if (level == 2) {
            System.out.println("HandlerB 处理了请求");
        } else if (next != null) {
            next.handleRequest(level);
        }
    }
}

// 测试代码
public class ChainDemo {
    public static void main(String[] args) {
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();

        handlerA.setNext(handlerB);

        handlerA.handleRequest(1); // HandlerA处理
        handlerA.handleRequest(2); // HandlerB处理
        handlerA.handleRequest(3); // 没有处理者
    }
}

运行效果

HandlerA 处理了请求
HandlerB 处理了请求

总结

优点:

  • 请求发送者与接收者解耦
  • 增加或调整处理者顺序灵活

缺点:

  • 请求可能一直传递而没有被处理
  • 调试链路时不易追踪

2. 命令模式(Command Pattern)

模式解释

命令模式核心思想是:

将请求封装成对象,从而使你可用不同的请求、队列或者日志来参数化其他对象,并支持可撤销操作。

适用场景:

  • 需要对操作进行记录、撤销、重做
  • 需要将请求发送者与接收者解耦

代码示例(Java,含测试代码)

// 命令接口
interface Command {
    void execute();
}

// 接收者
class Light {
    public void on() {
        System.out.println("灯打开了");
    }
    public void off() {
        System.out.println("灯关闭了");
    }
}

// 具体命令:打开灯
class LightOnCommand implements Command {
    private Light light;
    public LightOnCommand(Light light) {
        this.light = light;
    }
    @Override
    public void execute() {
        light.on();
    }
}

// 具体命令:关闭灯
class LightOffCommand implements Command {
    private Light light;
    public LightOffCommand(Light light) {
        this.light = light;
    }
    @Override
    public void execute() {
        light.off();
    }
}

// 调用者
class RemoteControl {
    private Command command;
    public void setCommand(Command command) {
        this.command = command;
    }
    public void pressButton() {
        command.execute();
    }
}

// 测试代码
public class CommandDemo {
    public static void main(String[] args) {
        Light light = new Light();
        Command onCommand = new LightOnCommand(light);
        Command offCommand = new LightOffCommand(light);

        RemoteControl remote = new RemoteControl();
        remote.setCommand(onCommand);
        remote.pressButton();

        remote.setCommand(offCommand);
        remote.pressButton();
    }
}

运行效果

灯打开了
灯关闭了

总结

优点:

  • 解耦请求发送者与执行者
  • 易于扩展新命令
  • 支持撤销、重做、日志等功能

缺点:

  • 增加了类的数量

3. 解释器模式(Interpreter Pattern)

模式解释

解释器模式核心思想是:

为某种语言定义文法,并建立一个解释器来解释该语言中的句子。

适用场景:

  • 需要解释特定规则、表达式(如正则、脚本、公式)
  • 语言文法规则相对稳定

代码示例(Java,含测试代码)

// 表达式接口
interface Expression {
    boolean interpret(String context);
}

// 终结符表达式
class TerminalExpression implements Expression {
    private String data;
    public TerminalExpression(String data) {
        this.data = data;
    }
    @Override
    public boolean interpret(String context) {
        return context.contains(data);
    }
}

// 或表达式
class OrExpression implements Expression {
    private Expression expr1;
    private Expression expr2;
    public OrExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }
    @Override
    public boolean interpret(String context) {
        return expr1.interpret(context) || expr2.interpret(context);
    }
}

// 测试代码
public class InterpreterDemo {
    public static void main(String[] args) {
        Expression java = new TerminalExpression("Java");
        Expression python = new TerminalExpression("Python");
        Expression javaOrPython = new OrExpression(java, python);

        System.out.println(javaOrPython.interpret("I love Java"));
        System.out.println(javaOrPython.interpret("I love Go"));
    }
}

运行效果

true
false

总结

优点:

  • 易于实现简单文法的解析
  • 扩展文法方便

缺点:

  • 对于复杂文法会导致类爆炸
  • 解释效率低

4. 迭代器模式(Iterator Pattern)

模式解释

迭代器模式核心思想是:

提供一种方法顺序访问聚合对象中的各个元素,而又不暴露其内部表示。

适用场景:

  • 需要遍历集合但不想暴露集合内部结构
  • 需要对遍历过程进行自定义

代码示例(Java,含测试代码)

import java.util.ArrayList;
import java.util.List;

// 自定义聚合类
class NameRepository {
    private List<String> names = new ArrayList<>();

    public void addName(String name) {
        names.add(name);
    }

    public Iterator getIterator() {
        return new NameIterator();
    }

    private class NameIterator implements Iterator {
        int index;
        @Override
        public boolean hasNext() {
            return index < names.size();
        }
        @Override
        public Object next() {
            return hasNext() ? names.get(index++) : null;
        }
    }
}

// 迭代器接口
interface Iterator {
    boolean hasNext();
    Object next();
}

// 测试代码
public class IteratorDemo {
    public static void main(String[] args) {
        NameRepository repo = new NameRepository();
        repo.addName("Jack");
        repo.addName("Tom");
        repo.addName("Lucy");

        Iterator it = repo.getIterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

运行效果

Jack
Tom
Lucy

总结

优点:

  • 遍历不暴露内部结构
  • 可为不同集合结构提供统一访问接口

缺点:

  • 对简单遍历可能显得多余

5. 中介者模式(Mediator Pattern)

模式解释

中介者模式核心思想是:

通过一个中介对象封装一系列对象之间的交互,使对象不需要显式地相互引用,从而降低耦合度。

适用场景:

  • 系统中对象之间存在复杂引用关系
  • 希望通过统一接口管理对象交互

代码示例(Java,含测试代码)

// 中介者接口
interface Mediator {
    void sendMessage(String message, Colleague colleague);
}

// 具体中介者
class ConcreteMediator implements Mediator {
    private ColleagueA colleagueA;
    private ColleagueB colleagueB;

    public void setColleagueA(ColleagueA colleagueA) {
        this.colleagueA = colleagueA;
    }
    public void setColleagueB(ColleagueB colleagueB) {
        this.colleagueB = colleagueB;
    }

    @Override
    public void sendMessage(String message, Colleague colleague) {
        if (colleague == colleagueA) {
            colleagueB.receive(message);
        } else {
            colleagueA.receive(message);
        }
    }
}

// 抽象同事类
abstract class Colleague {
    protected Mediator mediator;
    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive(String message);
}

// 具体同事A
class ColleagueA extends Colleague {
    public ColleagueA(Mediator mediator) {
        super(mediator);
    }
    public void send(String message) {
        mediator.sendMessage(message, this);
    }
    @Override
    public void receive(String message) {
        System.out.println("ColleagueA 收到消息: " + message);
    }
}

// 具体同事B
class ColleagueB extends Colleague {
    public ColleagueB(Mediator mediator) {
        super(mediator);
    }
    public void send(String message) {
        mediator.sendMessage(message, this);
    }
    @Override
    public void receive(String message) {
        System.out.println("ColleagueB 收到消息: " + message);
    }
}

// 测试代码
public class MediatorDemo {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();
        ColleagueA a = new ColleagueA(mediator);
        ColleagueB b = new ColleagueB(mediator);

        mediator.setColleagueA(a);
        mediator.setColleagueB(b);

        a.send("你好,B");
        b.send("你好,A");
    }
}

运行效果

ColleagueB 收到消息: 你好,B
ColleagueA 收到消息: 你好,A

总结

优点:

  • 降低对象之间的耦合
  • 便于维护对象交互逻辑

缺点:

  • 中介者可能变成“上帝类”,逻辑过多导致复杂度高

6. 备忘录模式(Memento Pattern)

模式解释

备忘录模式核心思想是:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复到原先保存的状态。

适用场景:

  • 需要保存对象的历史状态,以便撤销、恢复
  • 需要防止外部直接访问对象的内部状态

代码示例(Java,含测试代码)

// 备忘录类
class Memento {
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}

// 发起人
class Originator {
    private String state;
    public void setState(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
    public Memento saveStateToMemento() {
        return new Memento(state);
    }
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

// 管理者
class CareTaker {
    private java.util.List<Memento> mementoList = new java.util.ArrayList<>();
    public void add(Memento state) {
        mementoList.add(state);
    }
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

// 测试代码
public class MementoDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        CareTaker careTaker = new CareTaker();

        originator.setState("State #1");
        careTaker.add(originator.saveStateToMemento());

        originator.setState("State #2");
        careTaker.add(originator.saveStateToMemento());

        originator.setState("State #3");

        originator.getStateFromMemento(careTaker.get(0));
        System.out.println("恢复到: " + originator.getState());
    }
}

运行效果

恢复到: State #1

总结

优点:

  • 提供了状态的保存与恢复机制
  • 封装了内部状态,保护数据安全

缺点:

  • 可能消耗较多内存

7. 观察者模式(Observer Pattern)

模式解释

观察者模式核心思想是:

定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知并自动更新。

适用场景:

  • 一个对象状态变化需要通知多个对象
  • 解耦事件通知机制

代码示例(Java,含测试代码)

import java.util.ArrayList;
import java.util.List;

// 观察者接口
interface Observer {
    void update(String message);
}

// 主题
class Subject {
    private List<Observer> observers = new ArrayList<>();
    public void attach(Observer observer) {
        observers.add(observer);
    }
    public void notifyAllObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

// 具体观察者
class ConcreteObserver implements Observer {
    private String name;
    public ConcreteObserver(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println(name + " 收到消息: " + message);
    }
}

// 测试代码
public class ObserverDemo {
    public static void main(String[] args) {
        Subject subject = new Subject();

        subject.attach(new ConcreteObserver("观察者A"));
        subject.attach(new ConcreteObserver("观察者B"));

        subject.notifyAllObservers("状态更新了");
    }
}

运行效果

观察者A 收到消息: 状态更新了
观察者B 收到消息: 状态更新了

总结

优点:

  • 建立了松耦合的通知机制
  • 支持广播通信

缺点:

  • 通知链可能比较长,影响性能

8. 状态模式(State Pattern)

模式解释

状态模式核心思想是:

允许对象在内部状态改变时改变它的行为,看起来好像修改了它的类。

适用场景:

  • 对象的行为依赖于它的状态
  • 状态切换逻辑复杂且多处出现

代码示例(Java,含测试代码)

// 状态接口
interface State {
    void handle(Context context);
}

// 具体状态A
class ConcreteStateA implements State {
    public void handle(Context context) {
        System.out.println("当前状态:A -> 切换到 B");
        context.setState(new ConcreteStateB());
    }
}

// 具体状态B
class ConcreteStateB implements State {
    public void handle(Context context) {
        System.out.println("当前状态:B -> 切换到 A");
        context.setState(new ConcreteStateA());
    }
}

// 上下文
class Context {
    private State state;
    public Context(State state) {
        this.state = state;
    }
    public void setState(State state) {
        this.state = state;
    }
    public void request() {
        state.handle(this);
    }
}

// 测试代码
public class StateDemo {
    public static void main(String[] args) {
        Context context = new Context(new ConcreteStateA());
        context.request();
        context.request();
    }
}

运行效果

当前状态:A -> 切换到 B
当前状态:B -> 切换到 A

总结

优点:

  • 将状态与行为分离,易于扩展新状态
  • 避免冗长的 if/else 逻辑

缺点:

  • 增加了类的数量

9. 策略模式(Strategy Pattern)

模式解释

策略模式核心思想是:

定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。

适用场景:

  • 需要在运行时选择不同的算法
  • 避免多重条件语句

代码示例(Java,含测试代码)

// 策略接口
interface Strategy {
    int doOperation(int num1, int num2);
}

// 具体策略:加法
class OperationAdd implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

// 具体策略:减法
class OperationSubtract implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

// 上下文
class ContextStrategy {
    private Strategy strategy;
    public ContextStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// 测试代码
public class StrategyDemo {
    public static void main(String[] args) {
        ContextStrategy context = new ContextStrategy(new OperationAdd());
        System.out.println("加法结果: " + context.executeStrategy(10, 5));

        context = new ContextStrategy(new OperationSubtract());
        System.out.println("减法结果: " + context.executeStrategy(10, 5));
    }
}

运行效果

加法结果: 15
减法结果: 5

总结

优点:

  • 提高算法的可扩展性和可维护性
  • 避免使用大量条件语句

缺点:

  • 客户端必须了解所有策略类

10. 模板方法模式(Template Method Pattern)

模式解释

模板方法模式核心思想是:

定义一个操作中算法的骨架,而将一些步骤延迟到子类中实现。

适用场景:

  • 多个类中有相同算法结构,但部分步骤不同
  • 希望在父类中定义流程,子类中扩展实现

代码示例(Java,含测试代码)

// 抽象类
abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    // 模板方法
    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }
}

// 具体类
class Football extends Game {
    void initialize() { System.out.println("足球游戏初始化"); }
    void startPlay() { System.out.println("足球游戏开始"); }
    void endPlay() { System.out.println("足球游戏结束"); }
}

// 测试代码
public class TemplateDemo {
    public static void main(String[] args) {
        Game game = new Football();
        game.play();
    }
}

运行效果

足球游戏初始化
足球游戏开始
足球游戏结束

总结

优点:

  • 封装了不可变的流程结构
  • 子类只需实现个性化步骤

缺点:

  • 父类对子类的约束较强

11. 访问者模式(Visitor Pattern)

模式解释

访问者模式核心思想是:

将作用于对象结构中的元素的操作分离出来,使得在不改变元素类的前提下增加新的操作。

适用场景:

  • 数据结构稳定,但需要频繁新增操作
  • 不希望在数据类中混入大量业务逻辑

代码示例(Java,含测试代码)

// 元素接口
interface Element {
    void accept(Visitor visitor);
}

// 具体元素A
class ElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String featureA() {
        return "特性A";
    }
}

// 具体元素B
class ElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String featureB() {
        return "特性B";
    }
}

// 访问者接口
interface Visitor {
    void visit(ElementA elementA);
    void visit(ElementB elementB);
}

// 具体访问者
class ConcreteVisitor implements Visitor {
    public void visit(ElementA elementA) {
        System.out.println("访问 ElementA: " + elementA.featureA());
    }
    public void visit(ElementB elementB) {
        System.out.println("访问 ElementB: " + elementB.featureB());
    }
}

// 测试代码
public class VisitorDemo {
    public static void main(String[] args) {
        Element[] elements = { new ElementA(), new ElementB() };
        Visitor visitor = new ConcreteVisitor();

        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

运行效果

访问 ElementA: 特性A
访问 ElementB: 特性B

总结

优点:

  • 新增操作方便
  • 解耦数据结构与操作

缺点:

  • 添加新元素类时需要修改所有访问者类

📌 下一篇设计模式专栏(五):设计模式在实际项目中的应用 —— 支付系统扩展与回调处理案例

扫码关注订阅号

Beyond Code 程序员公众号二维码.jpg