【设计模式深度剖析】【9】【行为型】【访问者模式】| 以博物馆的导览员为例加深理解

👈️上一篇:备忘录模式

设计模式-专栏👈️


访问者模式

访问者模式(Visitor Pattern)就像一位多才多艺的导游,能够根据不同的景点(数据结构中的元素)提供个性化的解说服务(操作),而无需改变景点的本质。

定义

英文原话

The Visitor pattern is a behavioral design pattern that allows you to add new operations to objects without changing their classes. A visitor is a class with methods that correspond to the classes of an object structure it visits. A client uses the visitor to perform the operations on the elements of the object structure.

直译

访问者模式是一种行为设计模式,它允许你在不改变对象类的情况下为对象添加新的操作。访问者是一个类,它包含的方法对应于它所访问的对象结构中的类。客户端使用访问者来对对象结构的元素执行操作。

如何理解呢?

想象一下你有一个装满各种形状(圆形、矩形、三角形)的盒子。现在你想对这些形状进行不同的操作,比如测量它们的面积或周长。使用访问者模式,你可以创建一个“测量工具”(即访问者),它知道如何与每种形状交互并获取所需的信息,而不需要改变形状本身。

访问者模式的角色

访问者模式中的角色包括:

  1. Visitor(访问者):这是一个接口或抽象类,声明了访问特定元素类时需要执行的操作。
  2. ConcreteVisitor(具体访问者):实现了Visitor接口或继承了Visitor抽象类,实现了对元素类中每个元素的访问操作。
  3. Element(元素):这是一个接口或抽象类,声明了一个接受访问者对象的方法(通常是accept)。
  4. ConcreteElement(具体元素):实现了Element接口或继承了Element抽象类,并实现accept方法以接受访问者的访问。
  5. ObjectStructure(对象结构):能够枚举它的元素,并可以提供一个高层接口以允许访问者访问它的元素。这通常是一个集合类,如列表或树。

类图

在这里插入图片描述

代码示例

package com.polaris.designpattern.list3.behavioral.pattern09.visitor.classicdemo;

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

// Visitor 接口
interface Visitor {
    void visit(ConcreteElementA element);

    void visit(ConcreteElementB element);
}

// ConcreteVisitor 类  
class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("Visiting ConcreteElementA: " + element.operationA());
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("Visiting ConcreteElementB: " + element.operationB());
    }
}

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

// ConcreteElementA 类  
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationA() {
        return "elementA info...";
    }
}

// ConcreteElementB 类  
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationB() {
        return "elementB info...";
    }
}

// ObjectStructure 类(这里简单用ArrayList作为示例)

class ObjectStructure {
    private List<Element> elements = new ArrayList<>();

    public void add(Element element) {
        elements.add(element);
    }

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

// 客户端代码  
public class VisitorPatternDemo {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.add(new ConcreteElementA());
        objectStructure.add(new ConcreteElementB());

        Visitor visitor = new ConcreteVisitor();
        objectStructure.accept(visitor);
    }
}
/* Output:
Visiting ConcreteElementA: elementA info...
Visiting ConcreteElementB: elementB info...
*///~

在这个示例中,我们有一个Visitor接口和两个具体的访问者方法(对应于两种不同类型的元素)。我们还有两个实现了Element接口的ConcreteElement类,它们各自有自己的操作。ObjectStructure类管理了一个Element对象的集合,并提供了一个accept方法来遍历这些对象并接受访问者的访问。在客户端代码中,我们创建了一个ObjectStructure对象,并向其中添加了两个不同类型的元素。然后,我们创建了一个ConcreteVisitor对象,并使用它来访问ObjectStructure中的所有元素。

访问者模式的应用

访问者模式是一种设计模式,它允许在不改变数据结构的前提下,为数据结构中的每个元素定义新的操作。这种模式在需要为数据结构中的元素添加新行为,但又不想修改这些元素所在类的情况下特别有用。

优点

  1. 扩展性好:当需要为数据结构中的元素添加新操作时,只需定义一个新的访问者类即可,无需修改原有类。
  2. 复用性好:访问者模式可以通过为不同的数据结构定义相同的访问者接口,实现操作的复用。
  3. 灵活性好:访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可以相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则:每个访问者只负责一种操作,使代码更加清晰和易于维护。

缺点

  1. 破坏封装:在访问者模式中,具体元素需要向访问者公开其内部状态和方法,这可能会破坏对象的封装性。
  2. 增加新数据结构困难:当需要为新的数据结构添加访问者时,需要在访问者接口中添加新的方法,这可能会增加代码的复杂性。
  3. 具体元素变更困难:如果具体元素的内部结构发生变化,可能需要修改所有相关的访问者类,这可能会增加维护成本。

使用场景

  1. 电商网站商品分类与操作:电商网站通常有大量的商品,这些商品可以按照不同的属性进行分类。使用访问者模式,可以定义一个访问者对象,它能够访问不同类型的商品对象,并根据需要执行不同的操作,如按照价格排序、根据品牌筛选等。
  2. 图形编辑器的操作处理:在图形编辑器中,有多种图形元素,如线条、圆形、矩形等。访问者模式允许定义一个访问者对象,它能够访问这些图形元素并执行不同的操作,如移动、缩放、旋转或改变颜色等。
  3. 编译器和解释器设计:在编译器或解释器的设计中,抽象语法树(AST)是一个常见的数据结构。使用访问者模式,可以为AST中的不同节点类型定义不同的访问者,以执行如类型检查、代码优化、代码生成等操作。

示例解析:博物馆的导览员

假设你是一家博物馆的导览员,博物馆里有很多不同类型的展品,如绘画、雕塑和古代文物。作为导览员,你需要为不同类型的展品提供不同的解说词。但是,展品的种类可能会随着时间增加或减少,而你不希望每次有新的展品加入时都要重新学习所有的解说词。

在这里,展品可以看作是数据结构中的元素,而导览员就是访问者。导览员(访问者)需要访问不同的展品(元素),并为它们提供解说词(操作)。

代码示例

下面是一个简单的Java代码示例,用于模拟这个场景:

package com.polaris.designpattern.list3.behavioral.pattern09.visitor.demo1;

// 展品接口
interface Artifact {
    void accept(TourGuide tourGuide);
}

// 绘画展品  
class Painting implements Artifact {
    private String name;

    public Painting(String name) {
        this.name = name;
    }

    @Override
    public void accept(TourGuide tourGuide) {
        tourGuide.visit(this);
    }

    public String getName() {
        return name;
    }
}

// 雕塑展品  
class Sculpture implements Artifact {
    private String name;

    public Sculpture(String name) {
        this.name = name;
    }

    @Override
    public void accept(TourGuide tourGuide) {
        tourGuide.visit(this);
    }

    public String getName() {
        return name;
    }
}

// 导览员(访问者)接口  
interface TourGuide {
    void visit(Painting painting);

    void visit(Sculpture sculpture);
    // 如果以后有更多类型的展品,可以在这里添加方法  
}

// 具体导览员类  
class EnglishTourGuide implements TourGuide {
    @Override
    public void visit(Painting painting) {
        System.out.println("This is a painting called " + painting.getName() + ". It is beautiful!");
    }

    @Override
    public void visit(Sculpture sculpture) {
        System.out.println("This is a sculpture called " + sculpture.getName() + ". It is impressive!");
    }
}

// 客户端代码  
public class MuseumDemo {
    public static void main(String[] args) {
        // 创建展品  
        Artifact painting = new Painting("Mona Lisa");
        Artifact sculpture = new Sculpture("Thinker");

        // 创建导览员  
        TourGuide tourGuide = new EnglishTourGuide();

        // 导览员访问展品  
        painting.accept(tourGuide);
        sculpture.accept(tourGuide);
    }
}

/* Output:
This is a painting called Mona Lisa. It is beautiful!
This is a sculpture called Thinker. It is impressive!
*///~

在这个示例中,Artifact是展品接口,PaintingSculpture是具体的展品类。TourGuide是导览员接口,EnglishTourGuide是具体的导览员类。当新的展品类型出现时,只需要实现Artifact接口并添加相应的accept方法,然后在TourGuide接口中添加对应的visit方法即可,而不需要修改已经存在的展品类和导览员类。这样,就实现了在不改变数据结构的前提下,为数据结构中的每个元素定义新的操作。


👈️上一篇:备忘录模式

设计模式-专栏👈️

最近更新

  1. TCP协议是安全的吗?

    2024-06-17 08:32:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-17 08:32:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-17 08:32:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-17 08:32:03       20 阅读

热门阅读

  1. 快速排序压缩算法2024年最新一种压缩算法

    2024-06-17 08:32:03       7 阅读
  2. 第66集《摄大乘论》

    2024-06-17 08:32:03       7 阅读
  3. 测试开发工程师<职业规划方向>

    2024-06-17 08:32:03       7 阅读
  4. electron录制工具-视频保存、编辑页面

    2024-06-17 08:32:03       7 阅读
  5. Leetcode 3187. Peaks in Array

    2024-06-17 08:32:03       7 阅读
  6. CentOS下的miniconda3安装

    2024-06-17 08:32:03       6 阅读
  7. jvm工具-jps、jstat、jmap、jstack

    2024-06-17 08:32:03       7 阅读