- 解耦数据结构和数据
- 三种角色
- 访问者IPeople:对数据结构的访问
- 被访问者IMoney:被访问的数据结构
- 容器BankContainer:用于装载数据结构,提供方法接受访问者访问所有的数据结构
- UML类图

public interface IMoney {
void accept(IPeople people);
int getMoney();
}
public class DollarMoney implements IMoney {
@Override
public void accept(IPeople people) {
System.out.println("DollarMoney取钱");
people.visit(this);
}
@Override
public int getMoney() {
return 10;
}
}
public class RmbMoney implements IMoney {
@Override
public void accept(IPeople people) {
System.out.println("RmbMoney取钱");
people.visit(this);
}
@Override
public int getMoney() {
return 20;
}
}
public interface IPeople {
void visit(IMoney money);
}
public class Chinese implements IPeople {
@Override
public void visit(IMoney money) {
System.out.println("我是中国人, 我来取钱了, 取了" + money.getMoney() + "元");
}
}
public class American implements IPeople {
@Override
public void visit(IMoney money) {
System.out.println("我是美国人, 我来取钱了, 取了" + money.getMoney() + "元");
}
}
public class BankContainer {
private List<IMoney> list;
public BankContainer() {
this.init();
}
private void init(){
list = new CopyOnWriteArrayList<>();
list.add(new RmbMoney());
list.add(new DollarMoney());
}
public void getMoney(IPeople people){
for (IMoney money : this.list) {
money.accept(people);
}
}
}
public class ClientVisitor {
public static void main(String[] args) {
BankContainer container = new BankContainer();
System.out.println("===========中国人取钱===========");
container.getMoney(new Chinese());
System.out.println("===========美国人取钱===========");
container.getMoney(new American());
}
}
- 输出结果

- 编译期和运行期
- 编译期:指程序文件编译为汇编程序,再由汇编程序编译为机器语言的过程, 在Java中编译期指程序被编译为.class文件
- 运行期:指程序真正执行的时期.
- 分派
- 分派是指根据对象的类型进行方法的选择, 在Java中对象的类型在编译期和运行期有可能是不同的, 所谓编译看左, 运行看右.
- 如 Money money = new Rmb(); 变量 money在编译期类型是 Money, 在运行期实际的类型是Rmb, 从而引出了分派的两种类型.
- 分派的分类
- 静态分派:也叫编译期分派,根据编译期的变量定义的类型决定程序执行的方法是哪个, 比如重载就是静态分派, Java中重载方法的参数类型不同、参数个数可能也不同, 所以静态分派说Java是静态多分派的.
- 动态分派:也叫运行期分派,根据运行期的变量实际的类型决定程序执行的方法是哪个, 比如重写就是动态态分派, Java中对象的实际类型只能对应一个重写方法, 所以说Java是动态单分派的.
- 访问者模式的伪动态双分派
- Java只支持动态单分派, 想要支持动态双分派必须基于代码层面的设计模式解决, 所以说访问者模式是伪动态双分派的,在ClientVisitor中第一次根据人的类型即【new BankContainer().getMoney(new Chinese())和 new BankContainer().getMoney(new American())】完成了第一次动态分派.将人的实际类型传递给了RmbMoney和DollarMoney的accept()在IMoney的子类(如RmbMoney)的accept()中又调用人的visit(this), 并将自己的this对象传入, 我们知道this对象代表本类的实例, 所以完成了第二次动态分派, 即调用了某人的visit()方法
- 分派的code演示
public class Money {
}
class Rmb extends Money {}
class Dollar extends Money {}
class Bank{
public void getMoney(Money money){
System.out.println("Money");
}
public void getMoney(Rmb rmb){
System.out.println("RMB");
}
public void getMoney(Dollar dollar){
System.out.println("Dollar");
}
}
class DispatchClient {
public static void main(String[] args) {
Money money = new Money();
Money rmb = new Rmb();
Money dollar = new Dollar();
Bank bank = new Bank();
System.out.println("=====静态分派=====");
bank.getMoney(money);
bank.getMoney(rmb);
bank.getMoney(dollar);
System.out.println("=====动态分派=====");
bank.getMoney(new Money());
bank.getMoney(new Rmb());
bank.getMoney(new Dollar());
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步