我的同事建议我写一个访客模式来导航AST.任何人都可以告诉我更多我将如何开始写它?
据我所知,AST中的每个Node都有visit()方法(?)以某种方式被调用(从哪里?).这总结了我的理解.
为了简化一切,假设我有节点Root,Expression,Number,Op和树是这个样子:
Root
|
Op(+)
/ \
/ \
Number(5) \
Op(*)
/ \
/ \
/ \
Number(2) Number(444)
Run Code Online (Sandbox Code Playgroud)
任何人都可以想到访问者模式将如何访问此树以产生输出:
5 + 2 * 444
Run Code Online (Sandbox Code Playgroud)
谢谢,Boda Cydo.
python compiler-construction parsing visitor abstract-syntax-tree
从C++ 11开始,我们可以创建可以接受任何参数序列的模板函数:
template <typename... Ts>
void func(Ts &&... ts) {
step_one(std::forward<Ts>(ts)...);
step_two(std::forward<Ts>(ts)...);
}
Run Code Online (Sandbox Code Playgroud)
但是,假设在每个参数具有相同类型的情况下调用我的函数真的是有意义的 - 任何数量的参数都可以.
这样做的最佳方法是什么,即在这种情况下是否有一种很好的方法来约束模板以生成一个很好的错误消息,或者理想情况下,func当参数不匹配时,可以避免参与重载解析?
如果它有帮助我可以使它真正具体:
假设我有一些结构:
struct my_struct {
int foo;
double bar;
std::string baz;
};
Run Code Online (Sandbox Code Playgroud)
现在,我希望能够执行以下操作:打印结构的成员以进行调试,序列化和反序列化结构,按顺序访问结构的成员等.我有一些代码可以帮助解决这个问题:
template <typename V>
void apply_visitor(V && v, my_struct & s) {
std::forward<V>(v)("foo", s.foo);
std::forward<V>(v)("bar", s.bar);
std::forward<V>(v)("baz", s.baz);
}
template <typename V>
void apply_visitor(V && v, const my_struct & s) {
std::forward<V>(v)("foo", s.foo);
std::forward<V>(v)("bar", s.bar);
std::forward<V>(v)("baz", s.baz);
}
template <typename V>
void apply_visitor(V && v, my_struct && s) { …Run Code Online (Sandbox Code Playgroud) 我一直在尝试通过使用C++模板来实现访问者模式来减少代码中的样板量.到目前为止,我已经想出了这个:
class BaseVisitor {
public:
virtual ~BaseVisitor() {}
};
template<typename T>
class Visitor : public BaseVisitor {
public:
virtual void visit(T& /* visitable */) = 0;
};
template<typename Derived>
class Visitable {
public:
void accept(Visitor<Derived>& visitor) {
visitor.visit(static_cast<Derived&>(*this));
}
};
Run Code Online (Sandbox Code Playgroud)
Visitable的每个子类都如下所示:
class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};
Run Code Online (Sandbox Code Playgroud)
最后,访客看起来像这样:
class Renderer : public Visitor<Mesh>, public Visitor<Text> {}
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都很好......现在问题在于:
for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
Object& object = static_cast<Object&>(*it); …Run Code Online (Sandbox Code Playgroud) 我需要编写一个实现访问者设计模式的程序.问题是基本访问者类是模板类.这意味着BaseVisited :: accept()将模板类作为参数,因为它使用'this'并且我需要'this'指向对象的正确运行时实例,它也需要是虚拟的.
我想知道是否有任何解决这个问题的办法.
template <typename T>
class BaseVisitor {
public:
BaseVisitor();
T visit(BaseVisited *visited);
virtual ~BaseVisitor();
}
class BaseVisited {
BaseVisited();
template <typename T>
virtual void accept(BaseVisitor<T> *visitor) { visitor->visit(this); }; // problem
virtual ~BaseVisited();
}
Run Code Online (Sandbox Code Playgroud) Demeter法则期望在类之间进行最松散的耦合.
这意味着在类中暴露的所有getter/setter中有90%必须被"删除"并替换为"包含行为"的方法.实际上,它对应于"告诉,不要问"的哲学,因为不希望客户通过糟糕的getter/setter方法来帮助处理行为本身.如果在其他地方使用相同的操作,这也会减少重复的代码.
如果我们想要尊重单一责任原则,这意味着有许多行为方法的大型课程和过度使用授权.
另一方面,访问者模式定义是:
访问者允许您定义新操作,而无需更改其操作的元素的类.
所以,乍一看,似乎与德米特法律的期望相反:
One(Visitor)意味着类结构提供getter/setter,以便Visitor可以修改对象的状态而不需要触及类本身.
其他(Demeter)鼓励将与对象直接相关的所有行为代码包含在同一个类中.
所以我的问题是:
我们何时可以考虑关闭一个类进行修改,从而停止在其上添加行为方法,因此更喜欢将它们添加到新创建的访问者中,客户端使用getter/setter而不是在初始类之前已经暴露的行为方法的风险很大?
我相信了解Decorator和Visitor设计模式的意图.
虽然我可以列出以下差异
当我深入思考时,我无法说服自己两者之间的真正区别.
我有重复相同代码const和非const版本的问题.我可以用一些代码来说明问题.这里有两个样本访问者,一个修改访问对象,另一个不访问.
struct VisitorRead
{
template <class T>
void operator()(T &t) { std::cin >> t; }
};
struct VisitorWrite
{
template <class T>
void operator()(const T &t) { std::cout << t << "\n"; }
};
Run Code Online (Sandbox Code Playgroud)
现在这里是一个聚合对象 - 这只有两个数据成员,但我的实际代码要复杂得多:
struct Aggregate
{
int i;
double d;
template <class Visitor>
void operator()(Visitor &v)
{
v(i);
v(d);
}
template <class Visitor>
void operator()(Visitor &v) const
{
v(i);
v(d);
}
};
Run Code Online (Sandbox Code Playgroud)
并且有一个函数来演示以上内容:
static void test()
{
Aggregate a;
a(VisitorRead());
const Aggregate …Run Code Online (Sandbox Code Playgroud) 假设我有一套接受访问者(访问者模式)的类,但由于这些类或特定访问者的性质,执行它们可能会抛出一个已检查的异常.
访客接受界面:
public interface Mammal
{
void accept(MammalVisitor visitor);
}
Run Code Online (Sandbox Code Playgroud)
访客界面:
public interface MammalVisitor
{
void visit(Cat m);
void visit(Dog m);
void visit(Cow m);
}
Run Code Online (Sandbox Code Playgroud)
哺乳动物的实施:
public class Cat implements Mammal
{
public void accept(MammalVisitor visitor)
{
visitor.visit(this);
}
}
Run Code Online (Sandbox Code Playgroud)
我们假设Dog&Cow的实现与Cat相同
现在假设我的访客是:
public class MammalPrinter implements MammalVisitor
{
private final Appendable out;
public MammalPrinter(Appendable out)
{
this.out = out;
}
@Override
public void visit(Cat m)
{
out.append("I'm a cat");
}
@Override
public void visit(Dog m)
{
out.append("I'm a dog");
} …Run Code Online (Sandbox Code Playgroud) 我一直认为对象需要数据和消息来对其进行操作.你什么时候想要一个对象的外在方法?有一个经验法则,你有一个访客?这假设您可以完全控制对象图.
我正在上课,我们刚刚了解了这些设计模式.但是我看不出它们之间有什么区别.它们听起来像是一样的,在抽象的类上创建具体的类.有人可以帮我解决这个疑问吗?谢谢 (:
design-patterns strategy-pattern visitor template-method-pattern