(免责声明:这些示例是在构建编译器的上下文中给出的,但是这个问题都是关于访问者模式的,并且不需要任何编译器理论知识.)我将通过Andrew Appel的Java中的现代编译器实现来尝试自学编译器理论(所以不,这不是家庭作业),我无法理解他想如何使用访客模式将AST转换为IR树.(注意:我在Python中这样做,所以我也可以学习Python,这就是为什么即将推出的示例不是Java的原因.)据我所知,访问者模式中的访问和接受方法是无效的设计类型,所以,如果我有类似的东西
class PlusExp(Exp):
def __init__(self, exp_left, exp_right):
self.exp_left = exp_left
self.exp_right = exp_right
def accept(self, v):
v.visit_plus_exp(self)
Run Code Online (Sandbox Code Playgroud)
那么我希望能够写一个像访问者的方法
def visit_plus_exp(self, plus_exp):
return BINOP(BinOp.PLUS,
plus_exp.exp_left.accept(self),
plus_exp.exp_right.accept(self))
Run Code Online (Sandbox Code Playgroud)
这会将两个子表达式转换为IR,然后将它们与表示加号表达式的BINOP链接起来.当然,这是不可能的,除非我修改所有的接受函数以返回额外的信息,这也很麻烦,因为有时你只是想要一个不返回任何内容的打印访问者.然而,本文坚持认为访问者是正确的方式,而在Java中,这意味着它可以在没有Python灵活性的情况下完成.我想不出任何不太令人难以置信的解决方案 - 任何人都可以启发我的预期设计吗?
访问者模式是在C++中完成方法参数类型识别(在参数上有效单一调度,而不是成员的类)的最快方法吗?我可能知道我要来调用尚未知亚型的元素的确切方法(S),所以不可避免地使更多的虚拟方法调用就像V::visit(A *)在A::accept(V &v) { v.visit(this); }是不可取的.
// Is the Visitor pattern recommended here? (E inherits D inherits B.)
class Foo {
public:
virtual void visit(B *) { result = 3; }
virtual void visit(D *) { result = 4; }
virtual void visit(E *) { result = 5; }
private:
int result;
}; // class Foo
// Need to add generic interface to B and its children ...
class B {
public:
virtual void accept(class Foo …Run Code Online (Sandbox Code Playgroud) 我正在寻找一个应该具有以下功能的Java x86反汇编程序库:
所以,如果我有一些像这样反汇编的代码:
MOV EAX, EBX
CALL 1234
JMP 88
Run Code Online (Sandbox Code Playgroud)
那么库应该为MOV,CALL和JMP创建三个对象.然后我实现了一个执行各种操作的访问者(例如:解释,转换为x64或指向另一个处理器体系结构的指令).
提前致谢.
我正在尝试使用访问者模式来执行我的编译器的AST操作,但我似乎无法找到一个可以正常工作的实现.
AST课程摘录:
class AstNode
{
public:
AstNode() {}
};
class Program : public AstNode
{
public:
std::vector<std::shared_ptr<Class>> classes;
Program(const std::vector<std::shared_ptr<Class>>&);
void accept(AstNodeVisitor& visitor) const { visitor.visit(*this); }
};
class Expression : public AstNode
{
public:
Expression() {}
};
class Method : public Feature
{
public:
Symbol name;
Symbol return_type;
std::vector<std::shared_ptr<Formal>> params;
std::shared_ptr<Expression> body;
Method(const Symbol&, const Symbol&, const std::vector<std::shared_ptr<Formal>>&,
const std::shared_ptr<Expression>&);
feature_type get_type() const;
};
class Class : public AstNode
{
public:
Symbol name;
Symbol parent;
Symbol filename;
std::vector<std::shared_ptr<Feature>> features; …Run Code Online (Sandbox Code Playgroud) 访问者模式(双分派)是在其自己的权利非常有用的模式,但它经常被仔细检查是否有新的成员加入到继承层次,这是一个有效点突破的接口.
但是在Java 8中引入默认方法之后,现在我们可以在接口中定义默认实现,客户端接口不会中断,客户端可以适当地优雅地采用更改的接口.
interface Visitor{
public void visit(Type1 type);
public void visit(Type2 type);
//added after the first version of visitor is released
default public void visit(NewType type){
//some default implementation
}
}
Run Code Online (Sandbox Code Playgroud)
现在使用默认方法,如果NewType将来引入新类型,则不会再破坏客户端代码.
这是否使访问者更容易接受和有用?
我必须为任务设计一个解决方案,我想使用理论上类似于C#的ExpressionVisitor的东西.
为了好奇,我打开了.NET源代码ExpressionVisitor来查看它.从那时起,我一直在想为什么.NET团队实现了访问者.
例如,MemberInitExpression.Accept看起来像这样:
protected internal override Expression Accept(ExpressionVisitor visitor) {
return visitor.VisitMemberInit(this);
}
Run Code Online (Sandbox Code Playgroud)
我 - 可能是菜鸟 - 问题是:它有意义吗?我的意思是,Accept方法本身不应该负责它如何实现访问本身?我的意思是我期待这样的事情(internal从外部去除可见性的可见性):
protected override Expression Accept(ExpressionVisitor visitor) {
return this.Update(
visitor.VisitAndConvert(this.NewExpression, "VisitMemberInit"),
visitor.Visit(this.Bindings, VisitMemberBinding)
);
}
Run Code Online (Sandbox Code Playgroud)
但是这个代码在base ExpressionVisitor的VisitMemberInit方法中,从中调用MemberInitExpression.Accept.所以在Accept这里似乎没有任何好处.
为什么不在基础中处理树ExpressionVisitor,忘记所有Accept方法呢?
我希望你理解我的观点,并希望有人能够了解这一实施背后的动机.可能我根本不了解访客模式?...
这是一个问题陈述:我们有接口/超级班学生和教师
学生有两个实现/子分支,ScienceStudent和PhysicalEducationStudent
老师有ScienceTeacher和PhysicalEducationTeacher.
我们想要实现一个方法getMeetingPoint(Student,Teacher t),它根据学生和教师的类型返回他们见面的地方.
举例来说,如果其ScienceStudent和ScienceTeacher他们在满足实验室 如果PEStudent和体育教师,他们在满足地面,如果它的一个ScienceStudent和体育教师,反之亦然,他们在满足食堂
我们可以写一个天真的方法,检查使用instanceof.但问题是,当教师或学生扩展并且难以维护时,这变得复杂.这样的事情:
public class MeetingPointDecider {
getMeetingPoint(Student s,Teacher t) {
if(s instanceof ScienceStudent && t instanceof ScienceTeacher) {
return "Lab";
} else if (s instanceof PhysicalEducationStudent && t instanceof PhysicalEducationTeacher) {
return "GRound";
}
.
.
.
}
}
Run Code Online (Sandbox Code Playgroud)
另一个选择是写一个工厂,它接受一个学生和一个教师,并返回类似MeetingPointDecision [Ground或Lab],但问题仍然存在.我们可以使用任何好的模式,在添加新类时我们不必修改现有的类(或最小的修改),Say instanceofScienceStudent我们有ChemistryStudent,PhysicsStudent和ChemistryLab,PhysicsLab.还有可能添加更多操作,这些操作根据学生和教师类型的不同而有所不同(其中访问者是一个选项,但不确定如何使用两个决定类来实现)
有人可以建议一个好方法来实现这个吗?
谢谢!
考虑Visitor_pattern的原因之一:
这种分离的实际结果是能够在不修改这些结构的情况下向现有对象结构添加新操作.
假设您没有第三方库的源代码,并且已在相关对象上添加了一个操作.
由于您没有对象,因此无法修改您的元素(第三方类)以添加访问者.
在这种情况下,双重调度是不可能的.
那么哪种选择通常是首选?
Option 1: 在第三方类之上扩展一个继承层次结构并实现模式如图所示双重调度?
对于扩展A类的给定B类层次结构,我将添加
ElementA extends A
ElementB extends B
Run Code Online (Sandbox Code Playgroud)
现在,ConcreteElements派生自ElementA而不是A类.
缺点:课程数量会增加.
Option 2: 使用Visitor类作为中心帮助程序类,并使用单个调度完成工作.
缺点:根据UML图,我们并没有真正关注Visitor模式.
如果我错了,请纠正.
根据我发现的代码,似乎访问者需要知道访问对象的结构并调用所需的子代.在某些情况下,即使访问类被修改,访问者仍希望继续工作,这似乎有点笨拙.
我想真正的问题是:它们是一种模式,其中枚举是由访问过的代码而不是访问者代码完成的?
我有一个类应该为每个成员变量调用一个访问者方法.像这样的东西:
class A{
int a, b, c;
public:
void accept(Visitor &visitor){
visitor.visit(a);
visitor.visit(b);
visitor.visit(c);
}
};
Run Code Online (Sandbox Code Playgroud)
如何void accept() const在没有代码重复的情况下使用相同的代码获取方法?
重复的显而易见的解决方案是添加一个方法:
void accept(Visitor &visitor) const {
visitor.visit(a);
visitor.visit(b);
visitor.visit(c);
}
Run Code Online (Sandbox Code Playgroud)
该方法具有我想要的含义,但我想避免代码重复.拥有这两种方法的原因是能够通过"阅读"访问者读取变量并accept很好地使用该方法const.然后非const accept将可用于"写入/更新"访问者.
visitor-pattern ×10
java ×5
c++ ×3
oop ×2
c# ×1
code-design ×1
disassembly ×1
enumeration ×1
python ×1
tree ×1
x86 ×1