设计模式(分类)        设计模式(六大原则)   

    创建型(5种)        工厂方法         抽象工厂模式        单例模式        建造者模式        原型模式

    结构型(7种)        适配器模式        装饰器模式        代理模式        ​​​​​​外观模式      桥接模式        组合模式       享元模式

    行为型(11种)      策略模式        模板方法模式        观察者模式        迭代器模式     责任链模式     命令模式    备忘录模式          状态模式         访问者模式        中介者模式 


访问者模式(Visitor Pattern)是一种行为设计模式,它表示一个作用于某对象结构中的各个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式适用于需要对一个对象结构中的对象进行很多不同类型的运算,而且施加运算的对象又不希望知道这些运算的具体实现的情况。

结构: 访问者模式的主要参与者有:

  1. Visitor(访问者):声明一个访问操作接口,该接口中的每个操作元素对应一个需要访问的对象类的元素。具体访问者则实现了这些操作。
  2. Element(元素):定义一个接受访问者的方法,一般称为accept(),它以一个访问者作为参数。
  3. ConcreteElement(具体元素):实现了Element中的accept()方法,通常会调用访问者的访问方法来完成对一个元素的操作。
  4. ObjectStructure(对象结构):能够枚举它的元素,提供一个接口让访问者可以访问它的元素。

原理: 

        访问者模式通过双分派机制实现。首先,客户端代码将 ConcreteElement 对象传递给访问者对象;然后,访问者对象根据 ConcreteElement 的类型来决定调用哪个具体的操作方法。这样,增加新的元素类型或新的访问操作都很方便,只需增加新的元素类和新的访问者类即可,原有的代码无需修改。

优缺点:

优点:

  • 扩展性好:增加新的操作变得容易,只需要增加新的访问者类即可,符合开闭原则。
  • 封装性好:将数据结构和操作分离,使得操作集合可独立变化。

缺点:

  • 违反了单一职责原则,将数据结构和算法耦合到了一起。
  • 对象结构变化困难:若要增加新的元素类,需要修改访问者接口及所有具体访问者类,违反了开闭原则。
  • 实现较为复杂,不易理解。

应用场景:

  • 需要对对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类。
  • 当需要对一个组合结构中的对象进行很多不同类型的操作,而操作算法又彼此独立时。

 代码示例(以Java为例)

// 访问者接口
interface Visitor {
    void visit(TextElement element);
    void visit(ImageElement element);
    void visit(TableElement element);
}

// 具体访问者:统计字符数
class CharacterCounter implements Visitor {
    int count = 0;
    @Override
    public void visit(TextElement element) {
        count += element.getText().length();
    }
    // 图片和表格访问不做处理
    @Override
    public void visit(ImageElement element) {}
    @Override
    public void visit(TableElement element) {}
    // 获取统计结果
    public int getCount() { return count; }
}

// 具体访问者:统计图片数量
class ImageCounter implements Visitor {
    int count = 0;
    @Override
    public void visit(TextElement element) {}
    @Override
    public void visit(ImageElement element) { count++; }
    @Override
    public void visit(TableElement element) {}
    // 获取统计结果
    public int getCount() { return count; }
}

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

// 具体元素:文本
class TextElement implements Element {
    private String text;
    public TextElement(String text) { this.text = text; }
    public String getText() { return text; }
    @Override
    public void accept(Visitor visitor) { visitor.visit(this); }
}

// 其他具体元素:图片、表格的定义省略...

// 对象结构:文档
class Document {
    private List<Element> elements;
    public Document() { elements = new ArrayList<>(); }
    public void add(Element element) { elements.add(element); }
    public void print(Visitor visitor) {
        for (Element e : elements) {
            e.accept(visitor);
        }
    }
}

// 示例使用
public class VisitorPatternDemo {
    public static void main(String[] args) {
        Document doc = new Document();
        doc.add(new TextElement("Hello, World!"));
        doc.add(new ImageElement());
        doc.add(new TableElement());

        Visitor counter1 = new CharacterCounter();
        doc.print(counter1);
        System.out.println("Total characters: " + counter1.getCount());

        Visitor counter2 = new ImageCounter();
        doc.print(counter2);
        System.out.println("Total images: " + counter2.getCount());
    }
}

在这个例子中,Document类代表对象结构,它包含不同类型的Element(如TextElementImageElementTableElement等)。通过访问者模式,我们能够为这些不同类型的元素定义新的操作(如统计字符数、图片数),而无需修改元素本身的类。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐