一篇学会Java 23种设计模式(下)

一篇学会Java 23种设计模式(下)

Java设计模式学习笔记(下) 本文是Java 23种设计模式中行为型模式学习笔记的后半部分,接上篇的责任链模式和命令模式,继续学习剩余的行为型设计模式。 解释器模式 (Interpreter Pattern) 什么是解释器模式? 解释器模式是一种行为型设计模式,它定义了一种语言的语法表示方法,并定

Java设计模式学习笔记(下)

本文是Java 23种设计模式中行为型模式学习笔记的后半部分,接上篇的责任链模式和命令模式,继续学习剩余的行为型设计模式。

解释器模式 (Interpreter Pattern)

什么是解释器模式?

解释器模式是一种行为型设计模式,它定义了一种语言的语法表示方法,并定义一个解释器来解释该语言中的句子。换句话说,解释器模式用来解决语法解析相关问题。

解释器模式的结构

解释器模式包含以下几个核心角色:

  • AbstractExpression(抽象表达式):声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
  • TerminalExpression(终结符表达式):实现与文法中的终结符相关联的解释操作。
  • NonterminalExpression(非终结符表达式):为文法中的非终结符实现解释操作。
  • Context(环境类):包含解释器之外的一些全局信息。
  • Client(客户端):构建表示该文法定义的语言中一个特定的句子的抽象语法树,调用解释操作。

代码示例

以一个简单的算术表达式解释器为例:

// 抽象表达式
interface Expression {
    int interpret();
}

// 数字表达式(终结符表达式)
class NumberExpression implements Expression {
    private int number;
    
    public NumberExpression(int number) {
        this.number = number;
    }
    
    @Override
    public int interpret() {
        return number;
    }
}

// 加法表达式(非终结符表达式)
class AddExpression implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;
    
    public AddExpression(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }
    
    @Override
    public int interpret() {
        return leftExpression.interpret() + rightExpression.interpret();
    }
}

// 减法表达式(非终结符表达式)
class SubtractExpression implements Expression {
    private Expression leftExpression;
    private Expression rightExpression;
    
    public SubtractExpression(Expression leftExpression, Expression rightExpression) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
    }
    
    @Override
    public int interpret() {
        return leftExpression.interpret() - rightExpression.interpret();
    }
}

// 客户端
public class InterpreterPatternDemo {
    public static void main(String[] args) {
        // 构建抽象语法树表示: (5 + 3) - 2
        Expression expression = new SubtractExpression(
            new AddExpression(
                new NumberExpression(5),
                new NumberExpression(3)
            ),
            new NumberExpression(2)
        );
        
        // 解释执行
        int result = expression.interpret();
        System.out.println("(5 + 3) - 2 = " + result);
    }
}

应用场景

  1. 编译器、解释器:用于解释特定的语言或规则
  2. 正则表达式解析:将正则表达式翻译成特定的语法树
  3. SQL解析器:解析SQL语句
  4. 公式计算引擎:例如Excel公式的解析与计算
  5. 脚本语言的实现

在Java中的应用

  • java.util.Pattern:正则表达式解析
  • java.text.Format:格式化文本的抽象类,其子类如DateFormat、MessageFormat等实现解释功能
  • javax.el.ELResolver:用于Java EE中表达式语言解析

优缺点

优点:

  • 易于改变和扩展文法
  • 每个语法规则可用一个类来表示,方便实现文法
  • 增加新的解释表达式简单

缺点:

  • 对于复杂文法难以维护
  • 执行效率较低,对于复杂的解释操作可能导致性能问题
  • 使用场景较为特定,不是很常用

迭代器模式 (Iterator Pattern)

什么是迭代器模式?

迭代器模式是一种行为型设计模式,提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

迭代器模式的结构

迭代器模式包含以下几个核心角色:

  • Iterator(迭代器):定义访问和遍历元素的接口。
  • ConcreteIterator(具体迭代器):实现迭代器接口,完成集合元素的遍历。
  • Aggregate(集合):定义创建迭代器的接口。
  • ConcreteAggregate(具体集合):实现创建迭代器的接口,返回一个具体迭代器的实例。

代码示例

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

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

// 集合接口
interface Container<T> {
    Iterator<T> getIterator();
}

// 具体集合类
class NameRepository implements Container<String> {
    private List<String> names = new ArrayList<>();
    
    public NameRepository() {
        names.add("Robert");
        names.add("John");
        names.add("Julie");
        names.add("Lora");
    }
    
    @Override
    public Iterator<String> getIterator() {
        return new NameIterator();
    }
    
    // 具体迭代器类
    private class NameIterator implements Iterator<String> {
        private int index;
        
        @Override
        public boolean hasNext() {
            return index < names.size();
        }
        
        @Override
        public String next() {
            if(this.hasNext()) {
                return names.get(index++);
            }
            return null;
        }
    }
}

// 客户端
public class IteratorPatternDemo {
    public static void main(String[] args) {
        NameRepository namesRepository = new NameRepository();
        
        for(Iterator<String> iter = namesRepository.getIterator(); iter.hasNext();) {
            String name = iter.next();
            System.out.println("Name: " + name);
        }
    }
}

应用场景

  1. 访问一个集合对象内容而无需暴露其内部表示
  2. 支持对集合对象的多种遍历方式
  3. 为遍历不同的集合结构提供一个统一的接口

在Java中的应用

  • java.util.Iterator:Java集合框架的核心接口
  • java.util.Enumeration:早期集合的迭代器
  • java.util.Scanner:用于扫描输入源中的token

优缺点

优点:

  • 支持以不同方式遍历集合
  • 简化集合接口
  • 在同一个集合上可以有多个遍历
  • 符合单一职责原则,集合与遍历分离

缺点:

  • 增加类的数量,一定程度上增加系统复杂性
  • 迭代过程中无法增删集合元素(Java的Iterator部分解决了这个问题)

中介者模式 (Mediator Pattern)

什么是中介者模式?

中介者模式是一种行为型设计模式,它通过一个中介对象来封装一系列对象之间的交互,从而使各对象不需要显式地相互引用,降低了它们之间的耦合度。

中介者模式的结构

中介者模式包含以下几个核心角色:

  • Mediator(中介者):定义了对象之间交互的接口。
  • ConcreteMediator(具体中介者):实现中介者接口,协调各同事对象之间的交互。
  • Colleague(同事类):每个同事类都知道中介者对象,但不知道其他同事对象。
  • ConcreteColleague(具体同事类):每个具体同事类只与中介者通信。

代码示例

// 中介者接口
interface ChatMediator {
    void sendMessage(String message, User user);
    void addUser(User user);
}

// 抽象用户类
abstract class User {
    protected ChatMediator mediator;
    protected String name;
    
    public User(ChatMediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
    }
    
    public abstract void send(String message);
    public abstract void receive(String message);
}

// 具体中介者
class ChatMediatorImpl implements ChatMediator {
    private List<User> users;
    
    public ChatMediatorImpl() {
        this.users = new ArrayList<>();
    }
    
    @Override
    public void addUser(User user) {
        this.users.add(user);
    }
    
    @Override
    public void sendMessage(String message, User user) {
        for(User u : users) {
            // 不给发送消息的用户发送消息
            if(u != user) {
                u.receive(message);
            }
        }
    }
}

// 具体用户
class UserImpl extends User {
    
    public UserImpl(ChatMediator mediator, String name) {
        super(mediator, name);
    }
    
    @Override
    public void send(String message) {
        System.out.println(this.name + " 发送消息: " + message);
        mediator.sendMessage(message, this);
    }
    
    @Override
    public void receive(String message) {
        System.out.println(this.name + " 收到消息: " + message);
    }
}

// 客户端
public class MediatorPatternDemo {
    public static void main(String[] args) {
        ChatMediator mediator = new ChatMediatorImpl();
        
        User user1 = new UserImpl(mediator, "张三");
        User user2 = new UserImpl(mediator, "李四");
        User user3 = new UserImpl(mediator, "王五");
        
        mediator.addUser(user1);
        mediator.addUser(user2);
        mediator.addUser(user3);
        
        user1.send("大家好!");
    }
}

应用场景

  1. 一组对象以定义良好但复杂的方式进行通信
  2. 多对多的关系交互
  3. 对象之间的通信模式经常改变
  4. 希望集中控制交互

在Java中的应用

  • java.util.concurrent.Executor:线程池的中介者
  • java.util.Timer:调度任务的中介者
  • java.lang.reflect.Method#invoke():反射调用的中介

优缺点

优点:

  • 减少子类生成
  • 降低各个同事类之间的耦合
  • 集中控制对象交互
  • 将各同事对象解耦

缺点:

  • 中介者可能会变得复杂,变成"上帝类"
  • 维护和修改中介者可能很困难

备忘录模式 (Memento Pattern)

什么是备忘录模式?

备忘录模式是一种行为型设计模式,它在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。

备忘录模式的结构

备忘录模式包含以下几个核心角色:

  • Originator(发起人):记录当前时刻的内部状态,并负责创建和恢复备忘录数据。
  • Memento(备忘录):负责存储发起人的内部状态,且可以防止发起人以外的对象访问备忘录。
  • Caretaker(管理者):负责保存备忘录,但不能对备忘录的内容进行操作或检查。

代码示例

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

// 备忘录类
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 List<Memento> mementoList = new ArrayList<>();
    
    public void add(Memento state) {
        mementoList.add(state);
    }
    
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

// 客户端
public class MementoPatternDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();
        
        // 设置状态并保存
        originator.setState("状态 #1");
        caretaker.add(originator.saveStateToMemento());
        
        // 设置新状态并保存
        originator.setState("状态 #2");
        caretaker.add(originator.saveStateToMemento());
        
        // 再设置新状态
        originator.setState("状态 #3");
        System.out.println("当前状态: " + originator.getState());
        
        // 恢复到状态1
        originator.getStateFromMemento(caretaker.get(0));
        System.out.println("恢复到状态1: " + originator.getState());
        
        // 恢复到状态2
        originator.getStateFromMemento(caretaker.get(1));
        System.out.println("恢复到状态2: " + originator.getState());
    }
}

应用场景

  1. 需要保存和恢复对象状态的场景
  2. 撤销/重做功能
  3. 快照功能
  4. 事务回滚

在Java中的应用

  • java.io.Serializable:可用于实现备忘录模式
  • javax.swing.text.JTextComponent#getDocument() 的撤销机制
  • java.util.Date:可作为备忘录存储时间状态

优缺点

优点:

  • 提供了状态回滚的途径
  • 实现了信息的封装,使得客户端不需要关心状态保存的细节
  • 简化了发起人,让发起人不需要管理和保存其内部状态的多个版本

缺点:

  • 如果状态需要完整保存,可能导致消耗大量内存
  • 发起人必须小心地向备忘录提供保存的状态

观察者模式 (Observer Pattern)

什么是观察者模式?

观察者模式是一种行为型设计模式,它定义了对象之间的一种一对多依赖关系,使得当一个对象状态发生改变时,其所有依赖者都会收到通知并自动更新。

观察者模式的结构

观察者模式包含以下几个核心角色:

  • Subject(主题):被观察的对象,维护观察者列表,提供添加、删除观察者的方法。
  • Observer(观察者):定义接收到通知时的更新接口。
  • ConcreteSubject(具体主题):状态改变时通知观察者。
  • ConcreteObserver(具体观察者):实现Observer接口,在得到通知时进行更新。

代码示例

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

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

// 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 具体主题
class NewsPublisher implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String latestNews;
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(latestNews);
        }
    }
    
    public void setNews(String news) {
        this.latestNews = news;
        notifyObservers();
    }
}

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

// 客户端
public class ObserverPatternDemo {
    public static void main(String[] args) {
        NewsPublisher publisher = new NewsPublisher();
        
        Observer subscriber1 = new NewsSubscriber("订阅者1");
        Observer subscriber2 = new NewsSubscriber("订阅者2");
        Observer subscriber3 = new NewsSubscriber("订阅者3");
        
        publisher.registerObserver(subscriber1);
        publisher.registerObserver(subscriber2);
        publisher.registerObserver(subscriber3);
        
        publisher.setNews("重大新闻:Java设计模式学习心得发布!");
        
        publisher.removeObserver(subscriber2);
        
        publisher.setNews("最新消息:观察者模式非常实用!");
    }
}

应用场景

  1. 当一个对象的改变需要同时改变其他对象
  2. 事件驱动系统
  3. 消息订阅和发布系统
  4. GUI系统中组件状态变更通知

在Java中的应用

  • java.util.Observable/Observer(已在Java 9中被弃用)
  • java.util.EventListener:事件监听器
  • javax.servlet.http.HttpSessionBindingListener:Session绑定的监听器
  • javax.servlet.http.HttpSessionAttributeListener:Session属性变更的监听器
  • java.beans.PropertyChangeListener:属性变更的监听

优缺点

优点:

  • 观察者和被观察者之间是抽象耦合
  • 建立了一套触发机制
  • 支持广播通信
  • 符合开闭原则

缺点:

  • 如果观察者太多,通知所有观察者会花费大量时间
  • 如果观察者和被观察者之间有循环依赖,可能导致系统崩溃
  • 观察者不知道其他观察者的存在,可能导致不必要的更新

状态模式 (State Pattern)

什么是状态模式?

状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为,使对象看起来好像修改了它的类。

状态模式的结构

状态模式包含以下几个核心角色:

  • Context(环境):定义客户端感兴趣的接口,维护一个当前状态实例。
  • State(状态):定义一个接口以封装与Context的一个特定状态相关的行为。
  • ConcreteState(具体状态):实现State接口,为Context的某个状态提供实现。

代码示例

// 状态接口
interface State {
    void doAction(Context context);
    String getState();
}

// 具体状态A
class StartState implements State {
    @Override
    public void doAction(Context context) {
        System.out.println("玩家处于开始状态");
        context.setState(this);
    }
    
    @Override
    public String getState() {
        return "开始状态";
    }
}

// 具体状态B
class StopState implements State {
    @Override
    public void doAction(Context context) {
        System.out.println("玩家处于停止状态");
        context.setState(this);
    }
    
    @Override
    public String getState() {
        return "停止状态";
    }
}

// 运行状态
class RunningState implements State {
    @Override
    public void doAction(Context context) {
        System.out.println("玩家处于运行状态");
        context.setState(this);
    }
    
    @Override
    public String getState() {
        return "运行状态";
    }
}

// 环境类
class Context {
    private State state;
    
    public Context() {
        // 默认状态
        state = null;
    }
    
    public void setState(State state) {
        this.state = state;
    }
    
    public State getState() {
        return state;
    }
}

// 客户端
public class StatePatternDemo {
    public static void main(String[] args) {
        Context context = new Context();
        
        StartState startState = new StartState();
        startState.doAction(context);
        System.out.println("当前状态: " + context.getState().getState());
        
        RunningState runningState = new RunningState();
        runningState.doAction(context);
        System.out.println("当前状态: " + context.getState().getState());
        
        StopState stopState = new StopState();
        stopState.doAction(context);
        System.out.println("当前状态: " + context.getState().getState());
    }
}

应用场景

  1. 对象行为根据状态改变而改变的场景
  2. 状态转换条件复杂的场景
  3. 状态迁移需要有约束条件的场景
  4. 避免使用大量的条件判断

在Java中的应用

  • javax.faces.lifecycle.LifeCycle#execute():JSF生命周期状态变化
  • java.util.Iterator:迭代器状态变化

优缺点

优点:

  • 封装了转换规则,并将状态转换显式化
  • 消除了大量的条件判断语句
  • 状态对象可共享
  • 符合开闭原则

缺点:

  • 状态模式会增加系统类和对象的个数
  • 状态模式的使用必然会增加系统的结构性和实现复杂度
  • 对"开闭原则"的支持不太好,增加新的状态类需要修改那些负责状态转换的源代码

策略模式 (Strategy Pattern)

什么是策略模式?

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

策略模式的结构

策略模式包含以下几个核心角色:

  • Strategy(策略):定义所有支持的算法的公共接口。
  • ConcreteStrategy(具体策略):实现了Strategy接口的具体算法。
  • Context(上下文):持有一个Strategy的引用,定义了一个接口来让Strategy访问它的数据。

代码示例

// 策略接口
interface PaymentStrategy {
    void pay(int amount);
}

// 具体策略A
class CreditCardPayment implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;
    
    public CreditCardPayment(String name, String cardNumber, String cvv, String dateOfExpiry) {
        this.name = name;
        this.cardNumber = cardNumber;
        this.cvv = cvv;
        this.dateOfExpiry = dateOfExpiry;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 元已通过信用卡支付");
    }
}

// 具体策略B
class PaypalPayment implements PaymentStrategy {
    private String emailId;
    private String password;
    
    public PaypalPayment(String emailId, String password) {
        this.emailId = emailId;
        this.password = password;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 元已通过PayPal支付");
    }
}

// 具体策略C
class WeChatPayment implements PaymentStrategy {
    private String id;
    
    public WeChatPayment(String id) {
        this.id = id;
    }
    
    @Override
    public void pay(int amount) {
        System.out.println(amount + " 元已通过微信支付");
    }
}

// 上下文
class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

// 客户端
public class StrategyPatternDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
        
        // 使用信用卡支付
        cart.setPaymentStrategy(new CreditCardPayment("张三", "1234567890123456", "123", "12/25"));
        cart.checkout(1000);
        
        // 使用PayPal支付
        cart.setPaymentStrategy(new PaypalPayment("zhangsan@email.com", "password"));
        cart.checkout(2000);
        
        // 使用微信支付
        cart.setPaymentStrategy(new WeChatPayment("zhangsan123"));
        cart.checkout(3000);
    }
}

应用场景

  1. 需要在运行时选择算法的场景
  2. 有多种算法实现,需要动态切换的场景
  3. 避免使用多重条件选择语句
  4. 一个类定义了多种行为,并通过多个条件判断来决定使用哪种行为的场景

在Java中的应用

  • java.util.Comparator:比较策略
  • javax.servlet.http.HttpServlet:service()方法基于请求类型分派给doGet/doPost等方法
  • java.util.concurrent.ThreadPoolExecutor:使用不同的拒绝策略

优缺点

优点:

  • 策略可以自由切换
  • 避免使用多重条件判断
  • 扩展性良好,增加新策略很方便
  • 符合开闭原则

缺点:

  • 客户端必须知道所有的策略类并自行决定使用哪个策略类
  • 会增加很多策略类和对象
  • 无法同时在多个策略中使用某些功能(除非使用继承)

模板方法模式 (Template Method Pattern)

什么是模板方法模式?

模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法的结构即可重新定义算法的某些特定步骤。

模板方法模式的结构

模板方法模式包含以下几个核心角色:

  • AbstractClass(抽象类):定义抽象方法和实现模板方法
  • ConcreteClass(具体子类):实现父类中的抽象方法

代码示例

// 抽象类
abstract class Game {
    // 模板方法
    public final void play() {
        // 初始化游戏
        initialize();
        
        // 开始游戏
        startPlay();
        
        // 结束游戏
        endPlay();
    }
    
    // 这些方法将由子类实现
    protected abstract void initialize();
    protected abstract void startPlay();
    protected abstract void endPlay();
}

// 具体子类A
class Cricket extends Game {
    @Override
    protected void initialize() {
        System.out.println("板球游戏初始化!");
    }
    
    @Override
    protected void startPlay() {
        System.out.println("板球游戏开始!");
    }
    
    @Override
    protected void endPlay() {
        System.out.println("板球游戏结束!");
    }
}

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

// 客户端
public class TemplatePatternDemo {
    public static void main(String[] args) {
        Game game = new Cricket();
        game.play();
        
        System.out.println();
        
        game = new Football();
        game.play();
    }
}

应用场景

  1. 多个类有相同的方法,且逻辑相同,但细节不同
  2. 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由各个子类实现
  3. 重构时,模板方法模式是一个常用的模式,把相同的代码抽取到父类中

在Java中的应用

  • java.io.InputStream, java.io.OutputStream, java.io.Reader, java.io.Writer:定义了基本的读写方法,子类实现具体功能
  • java.util.AbstractList, java.util.AbstractSet, java.util.AbstractMap:集合框架中的模板实现
  • javax.servlet.http.HttpServlet:service()方法是一个模板方法,定义了处理HTTP请求的骨架流程

优缺点

优点:

  • 封装不变部分,扩展可变部分
  • 提取公共代码,便于维护
  • 行为由父类控制,子类实现
  • 符合开闭原则

缺点:

  • 每一个不同的实现都需要一个子类,导致类的个数增加
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,导致一种反向的控制结构
  • 可能导致复杂的继承关系

访问者模式 (Visitor Pattern)

什么是访问者模式?

访问者模式是一种行为型设计模式,它表示一个作用于某对象结构中的各元素的操作。它可以在不改变各元素类的前提下定义新的操作。

访问者模式的结构

访问者模式包含以下几个核心角色:

  • Visitor(访问者):声明了一组访问操作,每个操作对应一种类型的元素。
  • ConcreteVisitor(具体访问者):实现了每个由Visitor声明的操作。
  • Element(元素):定义一个accept操作,它以一个访问者为参数。
  • ConcreteElement(具体元素):实现accept操作。
  • ObjectStructure(对象结构):能够枚举它的元素,提供一个高层接口让访问者访问它的元素。

代码示例

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

// 元素接口
interface ComputerPart {
    void accept(ComputerPartVisitor visitor);
}

// 具体元素A
class Keyboard implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor visitor) {
        visitor.visit(this);
    }
}

// 具体元素B
class Monitor implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor visitor) {
        visitor.visit(this);

// 对象结构
class Computer implements ComputerPart {
    private List<ComputerPart> parts;
    
    public Computer() {
        parts = new ArrayList<>();
        parts.add(new Keyboard());
        parts.add(new Monitor());
        parts.add(new Mouse());
    }
    
    @Override
    public void accept(ComputerPartVisitor visitor) {
        for (ComputerPart part : parts) {
            part.accept(visitor);
        }
        visitor.visit(this);
    }
}

// 访问者接口
interface ComputerPartVisitor {
    void visit(Computer computer);
    void visit(Mouse mouse);
    void visit(Keyboard keyboard);
    void visit(Monitor monitor);
}

// 具体访问者A
class ComputerPartDisplayVisitor implements ComputerPartVisitor {
    @Override
    public void visit(Computer computer) {
        System.out.println("显示电脑");
    }
    
    @Override
    public void visit(Mouse mouse) {
        System.out.println("显示鼠标");
    }
    
    @Override
    public void visit(Keyboard keyboard) {
        System.out.println("显示键盘");
    }
    
    @Override
    public void visit(Monitor monitor) {
        System.out.println("显示显示器");
    }
}

// 具体访问者B
class ComputerPartMaintenanceVisitor implements ComputerPartVisitor {
    @Override
    public void visit(Computer computer) {
        System.out.println("维护电脑");
    }
    
    @Override
    public void visit(Mouse mouse) {
        System.out.println("维护鼠标");
    }
    
    @Override
    public void visit(Keyboard keyboard) {
        System.out.println("维护键盘");
    }
    
    @Override
    public void visit(Monitor monitor) {
        System.out.println("维护显示器");
    }
}

// 客户端
public class VisitorPatternDemo {
    public static void main(String[] args) {
        ComputerPart computer = new Computer();
        
        // 使用显示访问者
        computer.accept(new ComputerPartDisplayVisitor());
        
        System.out.println("------------------------");
        
        // 使用维护访问者
        computer.accept(new ComputerPartMaintenanceVisitor());
    }
}

应用场景

  1. 对象结构中的元素有不同的类型,而且需要对这些元素执行不同的操作
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而不想让这些操作"污染"这些对象的类
  3. 定义对象结构的类很少变化,但经常需要在此结构上定义新的操作

在Java中的应用

  • javax.lang.model.element.Element 和 javax.lang.model.element.ElementVisitor:用于语言元素的处理
  • javax.lang.model.type.TypeMirror 和 javax.lang.model.type.TypeVisitor:类型映射访问
  • java.nio.file.FileVisitor 和 SimpleFileVisitor:文件访问者接口

优缺点

优点:

  • 符合单一职责原则:将数据结构与数据操作分离
  • 优秀的扩展性:添加新的操作很方便
  • 良好的复用性和灵活性

缺点:

  • 增加新的元素类很困难:需要修改所有的访问者
  • 破坏封装:访问者模式要求元素对象暴露足够多的信息给访问者
  • 违反了依赖倒置原则:依赖了具体类而非抽象

总结

至此,我们已经学习了全部23种GoF设计模式中的行为型设计模式。行为型模式主要关注对象之间的通信,让系统更加灵活,更易于扩展和维护。

理解和掌握这些设计模式,能够帮助我们写出更加健壮、灵活且可维护的代码。在实际应用中,我们应该根据具体的业务场景选择合适的设计模式,而不是为了使用设计模式而使用设计模式。

最后,希望这些学习笔记能够帮助大家更好地理解和应用Java设计模式!


参考资料:

  1. 《设计模式:可复用面向对象软件的基础》 - Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. 《Head First 设计模式》 - Eric Freeman, Elisabeth Robson
  3. 《Java设计模式》 - Steven John Metsker, William C. Wake
  4. Java Design Patterns
  5. Spring Framework源码
Comment