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的方法。