# 访问者模式

# 概念

访问者模式是一种行为设计模式,允许在不修改已有代码的情况下,动态地添加新的操作到对象结构中。它将数据结构与操作解耦,使得可以独立地定义作用于复杂对象结构的操作。

# 作用

1.解耦数据结构与操作:将作用于对象结构的操作与对象结构本身分离,使两者可以独立变化。

2.符合单一职责原则:将相关操作集中到访问者类中,避免对象类承担过多职责。

3.符合开闭原则:新增操作时无需修改对象类代码,只需添加新的访问者类即可。

4.支持动态扩展:可在运行时通过更换访问者实例,灵活切换对象结构的操作逻辑。

# 场景

1.对象结构包含多个类对象,且需对这些对象实施依赖其具体类型的操作。

2.需对对象结构执行大量不相关操作,且希望避免操作“污染”对象类。

3.对象结构类型稳定但操作频繁变化,需经常新增操作而少修改对象结构。

4.需集中管理作用于对象结构的所有操作,便于维护和扩展。

# 举例

以下为访问者模式的Java代码示例:

// 访问者接口
interface ComputerPartVisitor {
    void visit(Mouse mouse);
    void visit(Keyboard keyboard);
    void visit(Display display);
    void visit(Computer computer);
}
// 具体访问者 - 清洁访问者
class CleanVisitor implements ComputerPartVisitor {
    @Override
    public void visit(Mouse mouse) { System.out.println("清洁鼠标"); }
    @Override
    public void visit(Keyboard keyboard) { System.out.println("清洁键盘"); }
    @Override
    public void visit(Display display) { System.out.println("清洁显示器"); }
    @Override
    public void visit(Computer computer) { System.out.println("清洁电脑"); }
}
// 具体访问者 - 升级访问者
class UpgradeVisitor implements ComputerPartVisitor {
    @Override
    public void visit(Mouse mouse) { System.out.println("升级鼠标驱动"); }
    @Override
    public void visit(Keyboard keyboard) { System.out.println("升级键盘驱动"); }
    @Override
    public void visit(Display display) { System.out.println("升级显示器驱动"); }
    @Override
    public void visit(Computer computer) { System.out.println("升级电脑系统"); }
}
// 元素接口
interface ComputerPart {
    void accept(ComputerPartVisitor visitor);
}
// 具体元素 - 鼠标
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); }
}
// 具体元素 - 显示器
class Monitor implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor visitor) { visitor.visit(this); }
}
// 具体元素 - 电脑(包含其他元素的容器)
class Computer implements ComputerPart {
    private List<ComputerPart> parts = new ArrayList<>();
    public Computer() {
        parts.add(new Mouse());
        parts.add(new Keyboard());
        parts.add(new Monitor());
    }
    @Override
    public void accept(ComputerPartVisitor visitor) {
        for (ComputerPart part : parts) part.accept(visitor);
        visitor.visit(this);
    }
}
// 测试类
public class VisitorPatternDemo {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.accept(new CleanVisitor());  // 输出清洁操作
        computer.accept(new UpgradeVisitor()); // 输出升级操作
    }
}

# 反例

不使用访问者模式时,通常会在对象类中直接添加操作方法:

// 鼠标类(含清洁和升级方法)
class Mouse {
    public void clean() { System.out.println("清洁鼠标"); }
    public void upgrade() { System.out.println("升级鼠标驱动"); }
}
// 键盘类(含清洁和升级方法)
class Keyboard {
    public void clean() { System.out.println("清洁键盘"); }
    public void upgrade() { System.out.println("升级键盘驱动"); }
}
// 显示器类(含清洁和升级方法)
class Monitor {
    public void clean() { System.out.println("清洁显示器"); }
    public void upgrade() { System.out.println("升级显示器驱动"); }
}
// 电脑类(管理子元素并调用其方法)
class Computer {
    private List<Object> parts = new ArrayList<>();
    public Computer() {
        parts.add(new Mouse());
        parts.add(new Keyboard());
        parts.add(new Monitor());
    }
    public void clean() {
        System.out.println("清洁电脑");
        for (Object part : parts) {
            if (part instanceof Mouse) ((Mouse)part).clean();
            else if (part instanceof Keyboard) ((Keyboard)part).clean();
            else if (part instanceof Monitor) ((Monitor)part).clean();
        }
    }
    public void upgrade() {
        System.out.println("升级电脑系统");
        for (Object part : parts) {
            if (part instanceof Mouse) ((Mouse)part).upgrade();
            else if (part instanceof Keyboard) ((Keyboard)part).upgrade();
            else if (part instanceof Monitor) ((Monitor)part).upgrade();
        }
    }
}
// 测试类
public class NoVisitorPatternDemo {
    public static void main(String[] args) {
        new Computer().clean();   // 输出清洁操作
        new Computer().upgrade(); // 输出升级操作
    }
}

此方式会导致对象类承担过多操作方法,新增操作时需修改所有对象类,违反开闭原则,且代码冗余度高。

# 原理

访问者模式通过“双分派”机制实现操作与对象的解耦:对象结构中的元素通过accept()方法接收访问者,并调用访问者的visit()方法,将自身类型作为参数传递。访问者根据元素类型执行对应操作,从而将操作逻辑集中到访问者类中,避免对象类因新增操作而频繁修改。

# 缺点

1.元素与访问者强耦合:元素类接口变化时,所有访问者类需同步修改。

2.破坏封装性:元素类需暴露内部细节(如通过accept()方法传递this引用)。

3.适用场景受限:对象结构元素类型频繁变化时,维护访问者成本高。

4.学习成本较高:双分派机制和接口设计对开发者要求较高。

总结

访问者模式通过解耦对象结构与操作,实现了操作的动态扩展,符合开闭原则和单一职责原则,适用于对象结构稳定但操作频繁变化的场景。其核心优势在于集中管理操作逻辑,但需权衡元素接口稳定性与维护成本,避免在元素类型多变的场景下过度使用。



微信公众号

QQ交流群
原创网站开发,偏差难以避免。

如若发现错误,诚心感谢反馈。

愿你倾心相念,愿你学有所成。

愿你朝华相顾,愿你前程似锦。