访问者模式

访问者模式是一种行为型模式,它最早是由Gof(Gang of Four,四人帮)在他们的《设计模式:可复用面向对象软件的基础》一书中提出的。
访问者模式的主要思想是,定义一个访问者类,可以遍历一个数据结构,然后在每个对象上执行相应的操作。访问者模式的实现需要设计两个部分:元素和访问者。元素是指数据结构中的每个对象,每个对象都要接受访问者访问;访问者是指定义了要执行的操作。
具体来说,访问者模式通常包括以下几个角色:抽象访问者、具体访问者、抽象元素、具体元素和对象结构。

抽象访问者:定义了要访问的每个对象的操作接口,即访问者可以访问哪些元素,以及对这些元素进行何种操作。
具体访问者:实现抽象访问者中定义的各个操作,即具体的访问方法。
抽象元素:定义了一个接受访问者访问的方法accept(),即元素可以接受哪些访问者,以及如何接受访问者。
具体元素:实现抽象元素中定义的accept()方法,即定义元素如何接受访问者的访问。
对象结构:维护一个元素集合,并且定义了遍历这些元素的方法,即定义了哪些元素可以被访问者访问。
访问者模式的优点在于,它可以将数据结构和数据操作分离开来,从而不会破坏数据结构的封装性。在访问者模式中,如果我们需要增加一些新的操作,只需要增加一个新的具体访问者,而不需要修改元素类的代码;反之亦然。
希望这个简单易懂的访问者模式的解释能够帮助你更好地理解和记忆它。

我们可以把访问者模式比喻成去动物园看动物。假设有一家动物园里有很多种动物,包括猴子、大象、长颈鹿等等。我们是游客,想要去看看这些动物,并且观察它们的行为。
在这个例子里,我们可以把动物园比喻成“对象结构”,而动物就是“元素”,我们就像是“访问者”一样来访问它们。我们不同于一般的游客,我们具有特殊的能力,即可以观察到它们的行为,比如说猴子爬树,大象用长鼻子吸水等等。在这个例子里,我们的“观察行为”就是“访问者要执行的操作”。
为了实现这个操作,我们需要一个“访问者接口”和几个“具体的访问者”,它们分别对应不同的观察行为,比如猴子爬树的行为,大象用长鼻子吸水的行为等等。同时,我们还需要一个“元素接口”,它定义了接受访问者的方法,让访问者可以对它进行观察。最后,我们需要一个“具体的元素”,它们对应不同的动物,比如猴子、大象、长颈鹿等等。
总的来说,在访问者模式中,我们将“元素”和“行为”进行了分离,这使得我们可以在不修改“元素”的情况下,为它们添加新的“行为”。这大大提高了程序的可扩展性,使得我们在开发过程中更加灵活、高效。

我们可以把访问者模式比喻成收割蔬菜的过程。假设有一块菜地里有很多种蔬菜,包括青菜、芹菜、茄子等等。我们是农民,想要去收割这些蔬菜,并且把它们带回家去。
在这个例子中,我们可以把菜地比作“对象结构”,而每种蔬菜就是“元素”,我们就像是“访问者”一样来收割它们。我们不同于一般的收割工人,我们具有特殊的技能,可以对每种蔬菜进行不同方式的收割,并且把它们带回家去加工。在这个例子中,我们的“收割方式”就是“访问者要执行的操作”。
为了实现这个操作,我们需要一个“访问者接口”和几个“具体的访问者”,它们分别对应不同的收割方式,比如摘青菜、打芹菜、摘茄子等等。同时,我们还需要一个“元素接口”,它定义了接受访问者的方法,让访问者可以对它进行收割。最后,我们还需要一个“具体的元素”,它们对应不同的蔬菜,比如青菜、芹菜、茄子等等。
总的来说,在访问者模式中,我们将“元素”和“操作”进行了分离,这使得我们可以在不修改“元素”的情况下,为它们添加新的“操作”,比如为每种蔬菜添加不同的加工方式。这样就提高了程序的可扩展性,让我们在开发和设计过程中更加灵活和高效。

在 Calcite 中,SqlNode 表示 SQL 预处理期间的内部语法树。我们可以将 SqlNode 视为“元素”,代表 SQL 语句中的不同语法部分,例如 SELECT 子句、WHERE 子句、JOIN 子句等。我们可以使用访问者模式来访问和操作 SqlNode 中不同的语法部分。
首先,我们需要定义一个“访问者接口”,例如 SqlVisitor 接口。这个接口定义了访问器需要访问的所有 SqlNode 的接口方法。例如,我们可能会具有访问 SELECT 子句、WHERE 子句、JOIN 子句等的接口方法。
接着,我们需要定义一个或多个“具体的访问者”,例如 SqlValidator。这个访问者可以执行与 SQL 预处理相关的验证和优化操作。此外,我们可能还有其他的访问者,例如 SqlToRelConverter,用于将 SqlNode 转换为逻辑计划树。
然后,我们需要定义一个“元素接口”,例如 SqlNodeVisitor。这个接口定义了用于访问 SqlNode 中不同语法部分的不同接口方法。例如,我们可能有访问 SqlSelect、SqlJoin、SqlLiteral 等节点的接口方法。
最后,我们需要定义具体的 SqlNode 实现,例如 SqlSelect、SqlJoin。每个 SqlNode 实现都提供了接受访问者的方法,例如 SqlSelect.accept(SqlNodeVisitor visitor)。这个方法会将访问控制权转移到访问者,让访问者可以对 SqlSelect 中的语法部分进行操作和访问。
总的来说,在 Calcite 中,我们通过访问者模式将“元素”(SqlNode)与“操作”(SqlVisitor)进行了分离。这使得我们可以在访问节点的基础上执行任意操作,从而实现了 Calcite 中 SQL 预处理的复杂逻辑。

参考资料:
https://m.runoob.com/design-pattern/visitor-pattern.html

posted @ 2023-04-21 11:20  积极向上的徐先生  阅读(41)  评论(0编辑  收藏  举报