Php 访问者模式对动态类型语言有用吗?
访问者模式允许在不扩展对象类的情况下编写对象上的操作。当然但为什么不编写一个全局函数或静态类,从外部操纵我的对象集合呢?基本上,在java这样的语言中,出于技术原因需要一个Php 访问者模式对动态类型语言有用吗?,php,python,ruby,design-patterns,visitor,Php,Python,Ruby,Design Patterns,Visitor,访问者模式允许在不扩展对象类的情况下编写对象上的操作。当然但为什么不编写一个全局函数或静态类,从外部操纵我的对象集合呢?基本上,在java这样的语言中,出于技术原因需要一个accept()方法;但是在一种不用accept()方法就可以实现相同设计的语言中,访问者模式会变得微不足道吗 说明:在Visitor模式中,可访问类(实体)有一个方法.accept(),其任务是在自身上调用访问者的.visit()方法。我可以看到java示例的逻辑:访问者为其支持的每个可访问类型定义了一个不同的.visit(
accept()
方法;但是在一种不用accept()
方法就可以实现相同设计的语言中,访问者模式会变得微不足道吗
说明:在Visitor模式中,可访问类(实体)有一个方法.accept()
,其任务是在自身上调用访问者的.visit()
方法。我可以看到java示例的逻辑:访问者为其支持的每个可访问类型定义了一个不同的.visit(n)
方法n
,并且在运行时必须使用.accept()
技巧从中进行选择。但是像python或php这样的语言具有动态类型,并且没有方法重载。如果我是访问者,我可以调用实体方法(例如,.serialize()
),而不知道实体的类型,甚至不知道方法的完整签名。(这就是“双重派遣”问题,对吗?)
我知道accept方法可以将受保护的数据传递给访问者,但这有什么意义呢?如果数据向访问者类公开,那么它实际上是类接口的一部分,因为它的细节在类之外很重要。无论如何,公开私人数据从来没有让我觉得是访问者模式的重点
因此,在python、ruby或php中,我似乎可以在访问对象中实现一个类似访问者的类,而不需要accept方法(也不需要反射),对吗?如果我可以处理一系列异构对象并调用它们的公共方法,而不需要“visited”类的任何合作,那么这仍然值得称为“Visitor模式”吗?模式的本质是否有我所缺少的东西,或者它只是归结为“编写一个从外部操纵对象以执行操作的新类”
另外,我已经看过很多关于某某和其他地方的讨论,但找不到任何能解决这个问题的东西。欢迎使用指针。也许,这取决于语言 visitor模式解决了不具有特性的语言中的双重和多重层次结构问题。以Ruby、Lisp和Python为例。它们都是动态类型语言,但在标准中只有CLOS Lisp实现了多分派。这也称为多方法,Python和Ruby显然可以通过使用扩展来实现它 我喜欢这种奇怪的评论,它说: Lisp的对象系统[CLOS]及其多分派并不能取代访问者模式, 但是仅仅提供了一个更简洁的实现,其中模式 消失了
在其他语言中,即使是静态类型的语言,也必须解决缺少多方法的问题。访问者模式就是这样一种方式 我认为您正在交替使用访问者模式和双重分派。当你说 如果我可以处理一系列异构对象并调用它们的公共方法,而不需要“visited”类的任何合作,那么这仍然值得称为“Visitor模式”吗 及 编写一个新类,从外部操纵对象以执行操作“ 您正在定义什么是双重分派。当然,访问者模式是通过双重分派实现的。但是,模式本身还有更多的东西
- 每个访问者都是一组元素(实体)上的算法,新访问者可以插入,而无需更改现有代码。打开/关闭原则
- 频繁添加新元素时,最好避免访问者模式
跟进评论 这是一个访问者psuedo代码,如果没有被访问对象的配合(至少没有开关块),我不知道如何实现它: visitor基础结构允许处理大量的命令子类型,无需选择大小写swithc(如果有) 关于处理枚举的访问者,我认为您这样限制了自己。这并不是说不能涉及协作类(抽象VisitorEnumerator) 例如,请注意,此访问者不知道枚举顺序:
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
}
}
这样就可以像这样重复使用它:
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
}
}
在实际代码中,我会为枚举器创建一个基类,然后将其子类化以处理不同的枚举场景,同时传入具体的Visitor子类以将它们完全解耦。希望您能看到保持枚举分离的威力。Visitor特别有用的地方是VisitoR需要切换访问者的类型,并且出于任何原因,您不想将这些知识编码到Visistee(认为插件架构)。请考虑下面的Python代码: 访客风格 (注意,我们可以使用基类/mixin压缩Visite逻辑) 与之相比: 非访客风格 在
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
}
}
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
}
}
class Banana(object):
def visit(self, visitor):
visitor.process_banana(self)
class Apple(object):
def visit(self, visitor):
visitor.process_apple(self)
class VisitorExample(object):
def process_banana(self, banana):
print "Mashing banana: ", banana
def process_banana(self, apple):
print "Crunching apple: ", apple
class NonVisitorVisitor(object):
def process(self, fruit):
verb = {Banana: "Mashing banana: ",
Apple: "Crunching apple: "}[type(fruit)]
print verb, fruit
def ASTNode__stringify(self):
text = str(self)
for child in self.children:
text += ", { " + child.stringify() + " }"
return text
def ASTConst__stringify(self):
text = str(self)
for child in self.children:
text += ", [ " + child.stringify() + " ]"
return text
def ASTIf__stringify(self):
text = str(self)
text += "__cond( " + self.op1.stringify() + ")"
text += "__then { " + self.op2.stringify() + "}"
text += "__else {" + self.op3.stringify() + "}"
return text