Visitor设计模式访问元素方法的问题
- GPT给出的答案
- 寻找灵感
- 前置声明Element层次的实例
- Visitor interface的声明
- Element interface的声明
- Element实际类的声明及实现
- 实现一个Visitor
- 客户端代码
- 实战
- 测试结果
针对C++来说,若要实现Visitor设计模式,则会面临循环声明问题。
Element接口的声明中,需要Visitor的声明;Visitor接口需要Element……若均使用include
宏则会导致至少一方无定义,进一步导致“不完全类型”错误。
但如果按照常规,仅在Visitor的声明上方采用单行声明方式添加Element实例的声明,就无法调用各个Element内部的特有方法了。
GPT给出的答案
#include <iostream>using namespace std;class Employee;
class HourlyEmployee;class IVisitor {
public:virtual void Visit(HourlyEmployee&) = 0;
};class Employee {
public:virtual void Accept(IVisitor& visitor) = 0;
};class HourlyEmployee : public Employee {
public:void Accept(IVisitor& visitor) override {visitor.Visit(*this);}int HourlyMethod() {return 0;}
};class PayrollVisitor : public IVisitor {
public:void Visit(HourlyEmployee& employee) override {cout << employee.HourlyMethod() << endl;}
};int main() {HourlyEmployee hourly_employee;PayrollVisitor payroll_visitor;hourly_employee.Accept(payroll_visitor);
}
源码存在一些问题,已修改,这个文件是可以正常编译的。
寻找灵感
从整个源码结构来看,按顺序分为四个部分
前置声明Element层次的实例
这一部分是为了让Visitor接口能正确声明
Visitor interface的声明
C++中不存在接口的概念,用抽象类模拟。(也就是带有纯虚函数)
Element interface的声明
Element实际类的声明及实现
当然在实际项目中,会把声明和定义分开。
实现一个Visitor
客户端代码
实战
基于上面的分析,我们可以将整个实现放在不同文件中。
目录结构:
在Nodes.h
中,声明Element层次
#pragma once#include "Visitor.h"class Base
{
public:virtual void accept(Visitor& v) = 0;
};class ClassA:public Base
{
public:ClassA() {}void accept(Visitor& v) override;int getid();
};class ClassB:public Base
{
public:ClassB() {}void accept(Visitor& v) override;int getidd();
};
注意:在Element层次的头文件中include Visitor接口的声明
在Nodes.cpp
中,实现这些Element
#include "Nodes.h"void ClassA::accept(Visitor& v)
{v.visit(*this);
}void ClassB::accept(Visitor& v)
{v.visit(*this);
}int ClassA::getid()
{return 1;
}int ClassB::getidd()
{return 2;
}
在Visitor.h
中,声明Visitor接口,并在接口前前置声明Element实际类
#pragma once#include <iostream>
using namespace std;class ClassA;
class ClassB;class Visitor
{
public:virtual void visit(ClassA& a) = 0;virtual void visit(ClassB& b) = 0;
};
注意:不要用包含的方式,要直接声明
另起一个文件,用来声明具体的Visitor:
RealVisitor.h
#pragma once#include "Visitor.h"class RealVisitor: public Visitor
{
public:void visit(ClassA& a) override;void visit(ClassB& b) override;
};
RealVisitor.cpp
实现它:
#include "RealVisitor.h"
#include "Nodes.h"void RealVisitor::visit(ClassA& a)
{cout << "a\n";cout << "aaa:" << a.getid() << endl;
}void RealVisitor::visit(ClassB& b)
{cout << "b\n";cout << "bbb:" << b.getidd() << endl;
}
注意:实现前务必在cpp文件前方采用include的方式包含Element具体类声明
因为实现Visitor的时候需要调用每个具体类的方法
主函数:main.cpp
#include "Nodes.h"
#include "RealVisitor.h"int main()
{ClassA a;ClassB b;RealVisitor v;a.accept(v);b.accept(v);Base &c=a;c.accept(v);
}
测试结果
a
aaa:1
b
bbb:2
a
aaa:1
可以看见,即使以Base类的身份调用accept,利用双重分发机制,也可以正确地调用Visitor的正确处理方法;Visitor的方法也可以正确地调用Element的方法。