【设计模式系列笔记】访问者模式

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 访问者模式是一种行为设计模式,旨在将算法与对象结构分离,使得能够在不修改元素类的前提下定义新的操作。这一模式的核心思想是在元素类中添加一个接受访问者的方法,从而实现在不同元素上执行不同操作的能力。

1. 访问者模式介绍

访问者模式是一种行为设计模式,旨在将算法与对象结构分离,使得能够在不修改元素类的前提下定义新的操作。这一模式的核心思想是在元素类中添加一个接受访问者的方法,从而实现在不同元素上执行不同操作的能力。

主要角色:

  1. 元素接口(Element): 定义了一个accept方法,该方法接受一个访问者对象作为参数,从而让访问者能够访问这个元素。
  2. 具体元素类(ConcreteElement): 实现了元素接口的具体类,同时包含了具体的业务逻辑。
  3. 访问者接口(Visitor): 定义了访问者能够访问各种具体元素的方法。
  4. 具体访问者类(ConcreteVisitor): 实现了访问者接口的具体类,包含了对不同元素的具体操作逻辑。

2. 关键思想

访问者模式的关键思想是将数据结构和数据操作分离,使得可以在不修改数据结构的情况下定义新的操作。该模式的核心目标是让算法的变化不影响数据结构的稳定性。

关键思想包括以下几点:

  1. 分离数据结构和操作: 访问者模式通过在数据结构中添加一个接受访问者的方法,将数据结构和操作分离。这使得我们可以定义新的操作,而无需修改现有的数据结构。
  2. 双重分发: 在访问者模式中,有一种双重分发的机制。元素对象在接受访问者时,将自身传递给访问者,然后访问者再根据具体元素的类型调用相应的方法。这种方式实现了多态性,使得不同元素能够调用访问者的不同方法。
  3. 增加新操作的灵活性: 由于新的操作是通过增加新的访问者来实现的,而不是修改元素类,因此访问者模式使得系统更容易扩展。如果需要增加新的操作,只需要创建新的访问者而不影响现有的元素类。
  4. 适用于数据结构稳定、操作变化频繁的场景: 访问者模式适用于那些数据结构相对稳定,但对数据操作进行频繁变更的情况。这种模式使得系统更容易维护和扩展。
  5. 可维护性和可扩展性: 通过将操作移动到访问者中,访问者模式提高了系统的可维护性和可扩展性。新增的操作不会影响到已有的元素类,使得系统更加灵活。

总的来说,访问者模式的关键思想是通过在元素类中引入接受访问者的方法,实现对数据结构和操作的解耦,从而提高系统的灵活性、可维护性和可扩展性。

3. 实现方式

示例代码

// 元素接口(Element)
interface Element {
    // 接受访问者的方法
    void accept(Visitor visitor);
}
// 具体元素A类
class ConcreteElementA implements Element {
    // 实现元素接口中的accept方法
    @Override
    public void accept(Visitor visitor) {
        // 调用访问者的visitElementA方法,并将自身作为参数传递给访问者
        visitor.visitElementA(this);
    }
    // 具体元素A的特有操作
    public void operationA() {
        // 输出具体元素A的特有操作,例如:在ConcreteElementA中执行操作A
        System.out.println("在ConcreteElementA中执行操作A");
    }
}
// 具体元素B类
class ConcreteElementB implements Element {
    // 实现元素接口中的accept方法
    @Override
    public void accept(Visitor visitor) {
        // 调用访问者的visitElementB方法,并将自身作为参数传递给访问者
        visitor.visitElementB(this);
    }
    // 具体元素B的特有操作
    public void operationB() {
        // 输出具体元素B的特有操作,例如:在ConcreteElementB中执行操作B
        System.out.println("在ConcreteElementB中执行操作B");
    }
}
// 访问者接口
interface Visitor {
    // 访问具体元素A的方法
    void visitElementA(ConcreteElementA elementA);
    // 访问具体元素B的方法
    void visitElementB(ConcreteElementB elementB);
}
// 具体访问者类
class ConcreteVisitor implements Visitor {
    // 实现访问具体元素A的方法
    @Override
    public void visitElementA(ConcreteElementA elementA) {
        // 输出访问者正在对具体元素A进行操作的信息
        System.out.println("访问者正在对ConcreteElementA进行操作");
        // 调用具体元素A的特有操作
        elementA.operationA();
    }
    // 实现访问具体元素B的方法
    @Override
    public void visitElementB(ConcreteElementB elementB) {
        // 输出访问者正在对具体元素B进行操作的信息
        System.out.println("访问者正在对ConcreteElementB进行操作");
        // 调用具体元素B的特有操作
        elementB.operationB();
    }
}
// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建具体元素A
        ConcreteElementA elementA = new ConcreteElementA();
        // 创建具体元素B
        ConcreteElementB elementB = new ConcreteElementB();
        // 创建具体访问者
        ConcreteVisitor visitor = new ConcreteVisitor();
        // 具体元素A接受访问者的访问
        elementA.accept(visitor);
        // 具体元素B接受访问者的访问
        elementB.accept(visitor);
    }
}

要点:

  1. 分离操作和数据结构: 访问者模式将操作(访问者)与数据结构(元素)分离开来,使得操作可以独立变化而不影响数据结构的设计,从而提高了系统的灵活性和可维护性。
  2. 开闭原则: 通过访问者模式,可以在不修改现有元素类的情况下,增加新的操作。这符合开闭原则,即对扩展开放,对修改关闭。
  3. 增加新操作的灵活性: 访问者模式可以轻松地增加新的操作,只需要添加新的访问者类即可,不需要修改现有元素类的代码。这使得系统具有较高的可扩展性。
  4. 适用于稳定的对象结构: 访问者模式适用于对象结构相对稳定但操作频繁变化的情况。如果对象结构经常变化,可能会导致需要频繁修改访问者接口和访问者类,降低了模式的灵活性和可维护性。
  5. 复杂性: 访问者模式可能会导致系统中增加了许多访问者类和元素类,从而增加了系统的复杂性。因此,在使用访问者模式时需要权衡设计的复杂性和灵活性之间的关系。
  6. 访问者对元素的侵入性: 在访问者模式中,为了让元素能够接受访问者的访问,通常需要在元素类中添加接受访问者的方法。这使得访问者对元素的侵入性较高,增加了元素类的复杂性。

注意事项:

  1. 访问者个数: 访问者模式适用于元素稳定而操作频繁变化的场景。如果元素结构经常变化,可能会导致访问者类的数量急剧增加,不利于维护。
  2. 破坏封装: 访问者模式可能破坏元素的封装性,因为具体访问者需要访问元素的内部状态。这可能违反了一些面向对象设计的原则。
  3. 适用场景: 访问者模式在以下场景中比较适用:
  • 元素的结构相对稳定,但对元素的操作经常发生变化。
  • 需要对一个对象结构中的元素进行很多不同且不相关的操作。
  • 系统有多个稳定的数据结构,而需要对这些数据结构进行统一的操作。
  1. 使用场景限制: 访问者模式不适用于在元素类中新增操作的情况,因为新增操作需要修改所有的访问者类。

总的来说,访问者模式是一种非常有用的模式,但在使用时需要权衡灵活性和封装性,以及根据具体情况选择是否使用。

优点:

  1. 分离关注点: 访问者模式将数据结构和对数据的操作分离,使得能够独立地改变元素的操作,而不影响元素本身的结构。
  2. 增加新操作: 新的操作可以通过创建新的访问者来轻松添加,而无需修改现有的元素类。这符合开放-封闭原则,对扩展开放,对修改封闭。
  3. 可扩展性: 可以在不修改元素结构的情况下,增加新的元素类型,同时通过创建新的访问者实现对这些新元素的操作。
  4. 多态性: 访问者模式利用多态性,通过动态绑定来调用相应元素的方法,提高了代码的可读性和可维护性。
  5. 逻辑集中: 相关的操作逻辑被集中在访问者的具体实现中,使得代码更加清晰、易懂。

缺点:

  1. 破坏封装: 访问者模式可能破坏元素的封装性,因为具体访问者需要访问元素的内部状态。这可能违反了一些面向对象设计的原则。
  2. 新增元素困难: 如果元素的结构经常变化,可能会导致需要创建大量的访问者类,使得系统变得复杂且难以维护。
  3. 违反依赖倒置原则: 具体访问者通常依赖于具体元素,这可能违反依赖倒置原则,即高层模块不应该依赖于低层模块,二者都应该依赖于抽象。

应用场景:

  1. 数据结构稳定、操作变化频繁: 当数据结构相对稳定,但对数据的操作经常变化时,访问者模式是一种合适的设计选择。
  2. 复杂对象结构: 当存在一个复杂的对象结构,且需要对其进行多种不同的操作时,访问者模式可以使得操作的变化更加灵活。
  3. 代码维护性要求高: 当系统要求对元素的操作进行扩展,但不希望修改现有代码时,访问者模式提供了一种可行的解决方案。
  4. 编译器、解释器等应用: 访问者模式常用于编译器、解释器等需要对抽象语法树进行操作的场景,其中抽象语法树的节点可以看作元素,而编译或解释的操作可以看作访问者。

总的来说,访问者模式在特定场景下能够提供一种清晰的结构,使得系统更易于维护和扩展。在使用时需要权衡其优缺点,并根据具体需求选择是否使用。

目录
相关文章
|
29天前
|
设计模式 Java
【设计模式系列笔记】责任链模式
责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它。每个处理者都有一个对下一个处理者的引用。责任链模式常用于处理请求的场景,例如在一个请求需要经过多个步骤或者多个对象来处理的情况下。
28 0
|
29天前
|
设计模式 缓存 监控
【设计模式系列笔记】代理模式
代理模式是一种结构型设计模式,它允许一个对象(代理对象)控制另一个对象的访问。代理对象通常充当客户端和实际对象之间的中介,用于对实际对象的访问进行控制、监控或其他目的。
50 1
|
21天前
|
设计模式 算法 Java
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
|
29天前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
17 2
|
29天前
|
设计模式
【设计模式】张一鸣笔记:责任链接模式怎么用?
【设计模式】张一鸣笔记:责任链接模式怎么用?
20 1
|
29天前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
38 4
|
29天前
|
设计模式 JavaScript 前端开发
vue的设计模式_笔记
vue的设计模式_笔记
17 0
|
29天前
|
设计模式 Go
[设计模式 Go实现] 行为型~访问者模式
[设计模式 Go实现] 行为型~访问者模式
|
29天前
|
设计模式 SQL 算法
【设计模式系列笔记】模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的骨架,并允许子类在不改变该算法结构的情况下重新定义算法的某些步骤。这种模式属于行为型模式,它通过将算法的不同部分封装在不同的方法中,从而使子类能够在不改变算法结构的前提下定制算法的某些步骤。
38 0
|
29天前
|
设计模式 算法 Java
【设计模式系列笔记】策略模式
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列的算法,将每个算法封装起来,并且使它们可以互相替换。策略模式使得算法可以独立于客户端而变化。
35 0