JS设计模式之访问者模式的用法详解

 更新时间:2023年08月30日 09:52:47   作者:慕仲卿  
JS访问者模式是一种行为型设计模式,用于将算法与对象结构分离, 该模式允许你定义新的操作(访问者)而无需修改现有对象结构(被访问者), 通过这种方式,你可以在不改变对象结构的情况下添加新的操作,本文就给大家详细的讲讲JS访问者模式的用法

定义和特点

访问者模式是一种行为型设计模式,用于将算法与对象结构分离。 该模式允许你定义新的操作(访问者)而无需修改现有对象结构(被访问者)。 通过这种方式,你可以在不改变对象结构的情况下添加新的操作。

参与者

在访问者模式中,有两个主要角色:被访问者和访问者。

  • 被访问者:一个具有一组**元素和方法(method)**的对象结构。
  • 访问者:一个能够对这些元素执行不同**操作(visit)**的对象。
  • 被访问者提供了**接受(accept)**访问者的方法,以便访问者能够在需要时访问元素。

使用流程

  • 首定义访问者接口,其中包含一组访问方法,每个方法对应一种操作。
  • 在被访问者接口中添加一个接受访问者的方法accept,以便访问者可以访问被访问者的元素。
  • 被访问者的具体实现类需要实现这个接收访问者的方法,并**将自身作为参数(this)**传递给访问者的具体访问方法。
  • 创建一个具体的访问者对象v,并将其传递给被访问者的接受方法,即accept(v)
  • 被访问者将根据传递的访问者对象调用相应的访问方法,从而执行特定的操作。 通过这种【改变新建不同结构的访问者,而不修改被访问者】的方式,你可以在不改变被访问者的结构的情况下,为其添加新的操作。

通俗的理解

访问者设计模式有两个部分完成,在实现不同目标的过程中保持其中一方(被访问者)的结构不变,只修改另外一方的结构(访问者)。 也就是牺牲一个保全另外一个。

作用

访问者模式允许你将算法与对象结构分离,并通过定义访问者接口和被访问者接口来实现多态性。 这种模式适用于需要对一个对象结构中的元素进行不同操作的场景,同时又希望保持对象结构的稳定性。

举例

// 被访问者接口:点心店
interface Bakery {
  accept(visitor: CustomerVisitor): void;
  make(): string;
  makePlus(): string;
}
// 具体的被访问者:圆点心店
class CirclePastry implements Bakery {
  // 被访问者的接受方法
  accept(visitor: CustomerVisitor): void {
    // 将自身作为参数(this)传递给访问者的具体访问方法
    visitor?.visitCirclePastry(this);
  }
  make(): string {
    return "制作圆点心";
  }
  makePlus(): string {
    return "制作精品圆点心";
  }
}
// 具体的被访问者:方点心店
class SquarePastry implements Bakery {
  accept(visitor: CustomerVisitor): void {
    visitor?.visitSquarePastry(this);
  }
  make(): string {
    return "制作方点心";
  }
  makePlus(): string {
    return "制作精品方点心";
  }
}
// 访问者接口:顾客
interface CustomerVisitor {
  visitCirclePastry(pastry: CirclePastry): void;
  visitSquarePastry(pastry: SquarePastry): void;
}
// 具体的访问者:点心爱好者
class PastryLover implements CustomerVisitor {
  visitCirclePastry(pastry: CirclePastry): void {
    console.log(`点心爱好者选择了${pastry.make()}`);
  }
  visitSquarePastry(pastry: SquarePastry): void {
    console.log(`点心爱好者选择了${pastry.make()}`);
  }
}
// 使用示例
const circlePastry: Bakery = new CirclePastry();
const squarePastry: Bakery = new SquarePastry();
const pastryLover: CustomerVisitor = new PastryLover();
circlePastry.accept(pastryLover); // 输出:点心爱好者选择了制作圆点心
squarePastry.accept(pastryLover); // 输出:点心爱好者选择了制作方点心
// 在保证被访问者结构的不变的前提下通过修改访问者的结构达到完成不同操作的目的
// 访问者接口:高级顾客
interface CustomerVisitorPlus {
  visitCirclePastry(pastry: CirclePastry): void;
  visitSquarePastry(pastry: SquarePastry): void;
}
// 具体的访问者:高级点心爱好者
class PastryLoverWithMoney implements CustomerVisitorPlus {
  visitCirclePastry(pastry: CirclePastry): void {
    console.log(`点心爱好者选择了${pastry.makePlus()}`);
  }
  visitSquarePastry(pastry: SquarePastry): void {
    console.log(`点心爱好者选择了${pastry.makePlus()}`);
  }
}
const pastryLoverWithMoney: PastryLoverWithMoney = new PastryLoverWithMoney();
circlePastry.accept(pastryLoverWithMoney); // 输出:点心爱好者选择了制作高级圆点心
squarePastry.accept(pastryLoverWithMoney); // 输出:点心爱好者选择了制作高级方点心
// 可以看出来circlePastry和squarePastry都被复用了

Babel插件中的使用

在 Babel 插件中修改 AST(抽象语法树)时,通常会使用访问者模式。

  • 定义访问者:定义一个访问者对象,该对象包含用于处理不同类型的 AST 节点的方法。每个方法对应一种 AST 节点类型,该方法将被调用以访问和处理相应类型的节点。
  • 遍历和修改 AST:通过使用 Babel 提供的遍历器(@babel/traverse),可以遍历整个 AST。在遍历过程中,对于每个访问到的节点,将根据节点的类型调用相应的访问者方法。
  • 修改 AST:在访问者方法中,您可以对 AST 进行修改。这可以涉及更改节点属性、替换节点、添加新节点等操作。通过修改 AST,插件可以实现源代码的转换和重写。

应用场景

  • DOM 操作:在浏览器中,DOM(文档对象模型)表示网页的结构和内容。使用访问者模式,您可以定义一个访问者对象,该对象可以遍历 DOM 树的节点,并执行相应的操作。例如,可以创建一个访问者来查找特定类型的节点、修改节点属性或样式,或执行其他与 DOM 相关的操作。
  • 数据结构操作:JavaScript 中有许多内置的数据结构,如数组、集合、映射等。通过使用访问者模式,您可以定义一个访问者对象,来对这些数据结构进行遍历和操作。例如,可以创建一个访问者来计算数组中的总和、过滤符合特定条件的元素,或者将映射转换为另一种形式。
  • 编译器和解析器:在编译器和解析器中,访问者模式经常被用来处理抽象语法树(AST)。通过定义访问者对象,可以遍历 AST 并执行各种语义分析、优化或代码生成操作。这样可以将复杂的编译器逻辑分离到不同的访问者方法中,使其更易于维护和扩展。
  • 事件处理:在浏览器中,事件处理是非常常见的任务。访问者模式可以用于处理不同类型的事件,并执行相应的操作。例如,可以创建一个访问者来处理鼠标事件、键盘事件或其他用户交互事件。
  • 数据校验和验证:当需要对数据进行复杂的校验和验证时,访问者模式可以提供一种结构化的方法。您可以定义一个访问者对象,该对象遍历数据结构并执行各种校验逻辑。这样可以将校验逻辑从数据结构中分离出来,使其更加可维护和可扩展。

以上就是JS设计模式之访问者模式的用法详解的详细内容,更多关于JS访问者模式的资料请关注脚本之家其它相关文章!

相关文章

  • Array栈方法和队列方法的特点说明

    Array栈方法和队列方法的特点说明

    本篇文章主要是对Array栈方法与队列方法的特点进行了详细的说明介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2014-01-01
  • setTimeout()递归调用不加引号出错的解决方法

    setTimeout()递归调用不加引号出错的解决方法

    用了setTimeout()想实现递归调用,如果第一个参数不加引号的话,就会出错,下面与大家分享下该如何解决
    2014-09-09
  • js实现简易计数器功能

    js实现简易计数器功能

    这篇文章主要为大家详细介绍了js实现计数器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 微信小程序中单位rpx和rem的使用

    微信小程序中单位rpx和rem的使用

    rpx是微信小程序新推出的一个单位,按官方的定义,rpx可以根据屏幕宽度进行自适应,在rpx出现之前,web页面的自适应布局已经有了多种解决方案,为什么微信还捣鼓出新的rpx单位?下面通过这篇文章来一起看看吧。
    2016-12-12
  • 将JSON字符串转换成Map对象的方法

    将JSON字符串转换成Map对象的方法

    下面小编就为大家带来一篇将JSON字符串转换成Map对象的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • javascript数组的定义及操作实例

    javascript数组的定义及操作实例

    在文章里小编给大家整理的是关于javascript数组的定义及操作的相关知识点,需要的朋友们学习下。
    2019-11-11
  • JavaScript数组去重的几种方法效率测试

    JavaScript数组去重的几种方法效率测试

    JavaScript数组去重是前端面试酷爱的问题,问题简单而又能看出程序员对计算机程序执行过程的理解如何。数组去重的方法有很多,到底哪种是最理想的我不清楚。于是我测试了下数组去重的效率。测试二十万个数据,随着数据越多效率很明显的就体验了出来。下面来一起看看吧。
    2016-10-10
  • js实现json数据行到列的转换的实例代码

    js实现json数据行到列的转换的实例代码

    为了实现这样的数据显示出来三个序列,分别为郑州、新乡、安阳的电量,就需要自己实现对这样数据的转换,转换成如下的形式:
    2013-08-08
  • ng-options和ng-checked在表单中的高级运用(推荐)

    ng-options和ng-checked在表单中的高级运用(推荐)

    AngularJS是当前非常的流行的前端框架,它的语法糖非常多,也极大的方便了前端开发者。这篇文章主要介绍了ng-options和ng-checked在表单中的高级运用,需要的朋友可以参考下
    2017-01-01
  • JavaScript高级程序设计(第三版)学习笔记6、7章

    JavaScript高级程序设计(第三版)学习笔记6、7章

    这篇文章主要介绍了JavaScript高级程序设计(第三版)学习笔记6、7章 的相关资料,需要的朋友可以参考下
    2016-03-03

最新评论