我一直在博客中看到对访客模式的引用,但我必须承认,我只是不明白.我阅读了维基百科文章的模式,我理解它的机制,但我仍然对我何时使用它感到困惑.
作为最近刚刚获得装饰模式的人,现在看到它在任何地方的用途我都希望能够直观地理解这个看似方便的模式.
所以,我只是在阅读访问者模式,我发现访问者和元素之间的来回非常奇怪!
基本上我们称之为元素,我们将其传递给访问者,然后元素将自身传递给访问者。然后访问者操作元素。什么?为什么?感觉太没必要了。我称之为“来回疯狂”。
因此,当需要在所有元素上实施相同的操作时,访问者的意图是将元素与其操作分离。这样做是为了防止我们需要用新动作扩展我们的元素,我们不想进入所有这些类并修改已经稳定的代码。所以我们在这里遵循开放/封闭原则。
为什么会有这一切来回,如果我们没有这些,我们会失去什么?
例如,我编写的这段代码记住了这个目的,但跳过了访问者模式的疯狂交互。基本上我有会跳跃和进食的动物。我想将这些动作与对象分离,所以我将动作移到了访客。吃和跳会增加动物的健康(我知道,这是一个非常愚蠢的例子......)
public interface AnimalAction { // Abstract Visitor
public void visit(Dog dog);
public void visit(Cat cat);
}
public class EatVisitor implements AnimalAction { // ConcreteVisitor
@Override
public void visit(Dog dog) {
// Eating increases the dog health by 100
dog.increaseHealth(100);
}
@Override
public void visit(Cat cat) {
// Eating increases the cat health by 50
cat.increaseHealth(50);
}
}
public class JumpVisitor implements AnimalAction { // ConcreteVisitor
public void visit(Dog dog) {
// Jumping increases the dog …Run Code Online (Sandbox Code Playgroud) 是否可以实现尊重开放/封闭原则的访客模式,但仍然能够添加新的可访问类?
开放/封闭原则规定"软件实体(类,模块,功能等)应该开放以进行扩展,但是关闭以进行修改".
struct ConcreteVisitable1;
struct ConcreteVisitable2;
struct AbstractVisitor
{
virtual void visit(ConcreteVisitable1& concrete1) = 0;
virtual void visit(ConcreteVisitable2& concrete2) = 0;
};
struct AbstractVisitable
{
virtual void accept(AbstractVisitor& visitor) = 0;
};
struct ConcreteVisitable1 : AbstractVisitable
{
virtual void accept(AbstractVisitor& visitor)
{
visitor.visit(*this);
}
};
struct ConcreteVisitable2 : AbstractVisitable
{
virtual void accept(AbstractVisitor& visitor)
{
visitor.visit(*this);
}
};
Run Code Online (Sandbox Code Playgroud)
您可以实现任意数量的派生自AbstractVisitor的类:它是可以扩展的.您无法添加新的可访问类,因为从AbstractVisitor派生的类将无法编译:它已关闭以进行修改.
AbstractVisitor类树遵循开放/封闭原则.AbstractVisitable类树不遵循开放/封闭原则,因为它无法扩展.
除了扩展AbstractVisitor和AbstractVisitable之外还有其他解决方案吗?
struct ConcreteVisitable3;
struct AbstractVisitor2 : AbstractVisitor
{
virtual void visit(ConcreteVisitable3& concrete3) = 0;
};
struct …Run Code Online (Sandbox Code Playgroud) c++ oop design-patterns visitor-pattern open-closed-principle
我必须写一个访问者模式来导航AST.任何人都可以告诉我更多我将如何开始写它?据我所知,AST中的每个节点都有visit()方法(?),它会以某种方式被调用(从哪里?).这总结了我的理解.为了简化一切,假设我有节点Root,Expression,Number,Op,树看起来像这样:
Root
|
Op(+)
/ \
/ \
Number(5) \
Op(*)
/ \
/ \
/ \
Number(2) Number(444)
Run Code Online (Sandbox Code Playgroud) 据我所知,访问者模式通常用于向某些层次结构添加方法.但我仍然没有得到它:看到我尝试突出显示左子树的示例:

Javascript树实现:
function node(val) {
this.value = val;
this.left = this.right = null;
}
var tree = new node("A");
tree.left = new node("B1");
tree.right = new node("B2");
tree.left.left = new node("C1");
tree.left.right = new node("C2");
Run Code Online (Sandbox Code Playgroud)
我想我正在使用访客模式突出显示:
node.prototype.accept = function(visitorObj) {
visitorObj.visit(this);
}
function visitor() {
var that = this;
this.visit = function(tgt) {
tgt.value = "*"+tgt.value;
}
this.highlight = function(tgt) {
tgt.accept(that);
if(tgt.left) that.highlight(tgt.left);
if(tgt.right) that.highlight(tgt.right);
}
}
(new visitor()).highlight(tree.left);
Run Code Online (Sandbox Code Playgroud)
但是为什么要使用接受访问方法,何时可以更直接?
function visitor() {
var that = this; …Run Code Online (Sandbox Code Playgroud) 我读了很多关于访客模式及其所谓的优点.然而,对我而言,在实践中应用它们似乎并没有那么多优点:
当你想要做的事实上是这样的时候,这一切似乎都是非常多的工作:
// Pseudocode
int SomeOperation(ISomeAbstractThing obj) {
switch (type of obj) {
case Foo: // do Foo-specific stuff here
case Bar: // do Bar-specific stuff here
case Baz: // do Baz-specific stuff here
default: return 0; // do some sensible default if type unknown or if we don't care
}
}
Run Code Online (Sandbox Code Playgroud)
我看到的唯一真正的优势(我在任何地方都没有看到过):访问者模式可能是在cpu时间方面实现上述代码片段的最快方法(如果你没有双重调度的语言或者以上述伪代码的方式进行有效的类型比较).
问题:
我写了访客模式如下,但我不明白什么是单一和双重调度.AFAIK,单个调度是基于调用者类型调用方法,其中double dispatch根据调用者类型和参数类型调用方法.
我想双重调度是在单个类层次结构中发生的,但是为什么访问者类有两个类层次结构但它仍然被认为是双重调度.
void floppyDisk::accept(equipmentVisitor* visitor)
{
visitor->visitFloppyDisk(this);
}
void processor::accept(equipmentVisitor* visitor)
{
visitor->visitProcessor(this);
}
void computer::accept(equipmentVisitor* visitor)
{
BOOST_FOREACH(equipment* anEquip, cont)
{
anEquip->accept(visitor);
}
visitor->visitComputer(this);
}
void visitFloppyDisk(floppyDisk* );
void visitProcessor(processor* );
void visitComputer(computer* );
Run Code Online (Sandbox Code Playgroud)
请使用我提供的示例代码进行解释.
AFAIK,第一次调度发生在调用accept的对象上,第二次调度发生在调用visit方法的对象上.
谢谢.
我知道访客模式是什么以及如何使用它; 这个问题是不是这个的副本一个.
我有一个库,我把大部分可重复使用的代码放在我写的,并链接到我的大多数项目.
我经常需要为某些类添加功能,但不将这些新功能添加到库中.让我用一个真实的例子:
在这个lib中,我有一个类Shape,继承自CircleShape,PolygonShape和CompositeShape.
我现在正在开发一个图形应用程序,我需要渲染它们Shape,但不想render在核心Shape类中放置虚函数,因为我使用的一些项目Shape不进行任何渲染,而其他图形项目可以使用不同的渲染引擎(我在这个项目中使用Qt,但对于我使用OpenGL的游戏,因此该render函数需要不同的实现).
最着名的方法是使用访客模式,当然,这会让我心中产生一些疑问:
任何类的任何库都需要像我Shape一样扩展.大多数公共图书馆(大约所有公共图书馆)都没有为访客模式提供任何支持; 为什么?我为什么要?
访问者模式是一种在C++中模拟Double Dispatching的方法.它在C++中不是原生的,需要显式实现,使得类接口更复杂:我认为applyVisitor函数不应该与我的类函数处于同一级别,我认为这就像打破抽象.
明确的向上铸造Shape用dynamic_cast比较昂贵,但对我来说,它看起来像一个清晰的解决方案.
所以我该怎么做?在我的所有库类中实现Double Dispatching?如果图书馆提供的Shape不是我的,但在互联网上找到了一些GPL库怎么办?
我已经学会了这两种模式但却不理解这两种模式之间的差异.
我不知道场景,何时何地使用这些模式.
任何人都可以解释差异和用例吗?