Visitor设计模式访问元素方法的问题

Visitor设计模式访问元素方法的问题

  • GPT给出的答案
  • 寻找灵感
    • 前置声明Element层次的实例
    • Visitor interface的声明
    • Element interface的声明
    • Element实际类的声明及实现
    • 实现一个Visitor
    • 客户端代码
  • 实战
  • 测试结果

针对C++来说,若要实现Visitor设计模式,则会面临循环声明问题。

Element接口的声明中,需要Visitor的声明;Visitor接口需要Element……若均使用include宏则会导致至少一方无定义,进一步导致“不完全类型”错误。
但如果按照常规,仅在Visitor的声明上方采用单行声明方式添加Element实例的声明,就无法调用各个Element内部的特有方法了。

GPT给出的答案

#include 

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设计模式访问元素方法的问题_第1张图片
这一部分是为了让Visitor接口能正确声明

Visitor interface的声明

C++中不存在接口的概念,用抽象类模拟。(也就是带有纯虚函数)
Visitor设计模式访问元素方法的问题_第2张图片

Element interface的声明

Visitor设计模式访问元素方法的问题_第3张图片

Element实际类的声明及实现

Visitor设计模式访问元素方法的问题_第4张图片
当然在实际项目中,会把声明和定义分开。

实现一个Visitor

Visitor设计模式访问元素方法的问题_第5张图片

客户端代码

Visitor设计模式访问元素方法的问题_第6张图片

实战

基于上面的分析,我们可以将整个实现放在不同文件中。
目录结构:
Visitor设计模式访问元素方法的问题_第7张图片
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 
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的方法。

你可能感兴趣的:(学习笔记,设计模式,c++,visitor,pattern)