访问者模式对动态类型语言有用吗? [英] Is the Visitor pattern useful for dynamically typed languages?

查看:94
本文介绍了访问者模式对动态类型语言有用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

访问者模式允许在不扩展对象类的情况下编写对象的操作。当然。但是为什么不写一个从外部操作我的对象集合的全局函数或静态类呢?基本上,在java语言中,出于技术原因需要一个 accept()方法;但是在一种语言中,我可以在没有 accept()方法的情况下实现相同的设计,访问者模式是否变得微不足道?

The Visitor pattern allows operations on objects to be written without extending the object class. Sure. But why not just write a global function, or a static class, that manipulates my object collection from the outside? Basically, in a language like java, an accept() method is needed for technical reasons; but in a language where I can implement the same design without an accept() method, does the Visitor pattern become trivial?

说明:在访客模式中,可访问类(实体)有一个方法 .accept(),其工作是调用访问者的 .visit ()方法。我可以看到java示例的逻辑:访问者为每个可访问类型定义了一个不同的 .visit(n)方法 n 它支持,并且必须在运行时使用 .accept()技巧来选择它们。但像python或php这样的语言有动态打字,没有方法重载。如果我是访问者,我可以调用实体方法(例如, .serialize()),而不必知道实体的类型,甚至是方法的完整签名。 (这是双重调度问题,对吗?)

Explanation: In the Visitor pattern, visitable classes (entities) have a method .accept() whose job is to call the visitor's .visit() method on themselves. I can see the logic of the java examples: The visitor defines a different .visit(n) method for each visitable type n it supports, and the .accept() trick must be used to choose among them at runtime. But languages like python or php have dynamic typing and no method overloading. If I am a visitor I can call an entity method (e.g., .serialize()) without knowing the entity's type or even the full signature of the method. (That's the "double dispatch" issue, right?)

我知道接受方法可以将受保护的数据传递给访问者,但是有何意义?如果这些数据暴露给访问者类,它实际上是类接口的一部分,因为它的细节在类之外。暴露私人数据从来没有触及我作为访问者模式的一点,无论如何,

I know an accept method could pass protected data to the visitor, but what's the point? If the data is exposed to the visitor classes, it is effectively part of the class interface since its details matter outside the class. Exposing private data never struck me as the point of the visitor pattern, anyway.

所以似乎在python,ruby或php中,我可以实现一个没有被访问对象中的接受方法(没有反射),对吧?如果我可以与一个异构对象的家庭工作,并且在没有被访问类的任何合作的情况下调用它们的公共方法,这仍然被称为访客模式?有没有什么是我缺少的模式的精髓,还是简单地将从外面写入一个新的类来操作你的对象来执行?

So it seems that in python, ruby or php I can implement a visitor-like class without an accept method in the visited object (and without reflection), right? If I can work with a family of heterogeneous objects and call their public methods without any cooperation from the "visited" class, does this still deserve to be called the "Visitor pattern"? Is there something to the essence of the pattern that I am missing, or does it just boil down to "write a new class that manipulates your objects from the outside to carry out an operation"?

PS。我看过很多关于SO和其他地方的讨论,但找不到任何解决这个问题的东西。指针欢迎。

PS. I've looked at plenty of discussion on SO and elsewhere, but could not find anything that addresses this question. Pointers welcome.

推荐答案

这个答案是由于对PHP等的无知而引起的,但是访问者通常需要调用不止一个方法(你提到serialize)在实体上。当访问()方法在具体的访问者上被调用时,访问者能够为每个实体子类型运行不同的代码。我看不到与动态类型语言有什么不同(虽然我会喜欢一些反馈)。

This answer is made with an ignorance of PHP etc but the Visitor needs typically to call more than just a single method (you mentioned "serialize") on the entities. When the Visit() method is called on the concrete Visitor, the Visitor is capable of running distictly different code for each entity subtype. I don't see how that is different from a dynamically-types language (though I'd love some feedback).

访问者的另一个好处是它提供了一个干净从枚举实体的代码中分离每个实体运行的代码。这在至少一个大型项目中为我节省了一些严重的代码重复。

Another nice benefit of Visitor is it provides a clean seperation of the code that is getting run on each entity from the code that enumerates the entities. This has saved me some serious code duplication in at least one large project.

除此之外,我使用的访问者没有方法重载的语言。你只需用VisitN(TypeN n)替换Visit(TypeN n)。

As an aside, I've used Visitor in languages that did not have method overloading. You just replace Visit(TypeN n) with VisitN(TypeN n).

从评论中追踪

psuedo代码,我不知道如何没有被访问对象的合作(至少没有切换块):

This is a visitor psuedo code, and I don't know how I would so it without the cooperation of the visited object (at least without a switch block):

abstract class ScriptCommand
{
   void Accept(Visitor v);
}

abstract class MoveFileCommand
{
   string TargetFile;
   string DestinationLocation;

   void Accept(Visitor v)
   {
      v.VisitMoveFileCmd(this);  // this line is important because it eliminates the switch on object type
   }
}

abstract class DeleteFileCommand
{
   string TargetFile;

   void Accept(Visitor v)
   {
      v.VisitDeleteFileCmd(this); // this line is important because it eliminates the switch on object type

   }
}

// etc, many more commands

abstract class CommandVisitor
{
   void VisitMoveFileCmd(MoveFileCommand cmd);
   void VisitDeleteFileCmd(DeleteFileCommand cmd);
   // etc
}

// concrete implementation

class PersistCommandVisitor() inherits CommandVisitor
{
   void VisitMoveFileCmd(MoveFileCommand cmd)
   {
      // save the MoveFileCommand instance to a file stream or xml doc
      // this code is type-specific because each cmd subtype has vastly
      // different properties
   }

   void VisitDeleteFileCmd(DeleteFileCommand cmd)
   { 
      // save the DeleteFileCommand instance to a file stream or xml doc
      // this code is type-specific because each cmd subtype has vastly
      // different properties
   }

}

访问者基础设施允许处理各种命令子类型,没有select case,swithc,否则。

The visitor infrastructure allows the handling of a wide array of command subtypes with no select case, swithc, if else.

对于处理枚举的访问者,我认为你是这样限制的。这不是说一个合作的类(一个抽象的VisitorEnumerator)不能参与。

In regards to the visitor handling the enumerating, I think you are limiting yourself like that. That's not to say a cooperating class (an abstract VisitorEnumerator) can't be involved.

例如,注意这个访问者不知道枚举的顺序:

For example, note this visitor is unaware of the order of enumeration:

class FindTextCommandVisitor() inherits CommandVisitor
{
   string TextToFind;
   boolean TextFound = false;

   void VisitMoveFileCmd(MoveFileCommand cmd)
   {
      if (cmd.TargetFile.Contains(TextToFind) Or cmd.DestinationLocation.Contains(TextToFind))
         TextFound = true;
   }


   void VisitDeleteFileCmd(DeleteFileCommand cmd)
   { 
      // search DeleteFileCommand's properties
   }

}

这样就可以像这样重用:

And this allows it to be reused like this:

ScriptCommand FindTextFromTop(string txt)
{
   FindTextCommandVisitor v = new FindTextCommandVisitor();
   v.TextToFind = txt;
   for (int cmdNdx = 0; cmdNdx < CommandList.Length; cmdNdx++)
   {
      CommandList[cmdNdx].Accept(v);
      if (v.TextFound)
         return CommandList[cmdNdx];  // return the first item matching
   }
}

,并枚举相同方式与同一访问者相同:

and the enumerate the opposite way with the same visitor:

ScriptCommand FindTextFromBottom(string txt)
{
   FindTextCommandVisitor v = new FindTextCommandVisitor();
   v.TextToFind = txt;
   for (int cmdNdx = CommandList.Length-1; cmdNdx >= 0; cmdNdx--)
   {
      CommandList[cmdNdx].Accept(v);
      if (v.TextFound)
         return CommandList[cmdNdx];  // return the first item matching
   }
}

在实际代码中将为枚举器创建一个基类,然后将其子类化以处理不同的枚举方案,同时传入具体的访问者子类以完全解耦它们。希望你能看到保持枚举单独的力量。

In real code I would create a base class for the enumerator and then subclass it to handle the different enumeration scenarios, while passing in the concrete Visitor subclass to completely decouple them. Hopefully you can see the power of keeping the enumeration seperate.

这篇关于访问者模式对动态类型语言有用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆