欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > 【第20节】C++设计模式(行为模式)-Visitor(访问者)模式

【第20节】C++设计模式(行为模式)-Visitor(访问者)模式

2025/3/9 3:08:34 来源:https://blog.csdn.net/linshantang/article/details/146094078  浏览:    关键词:【第20节】C++设计模式(行为模式)-Visitor(访问者)模式

一、问题引出

        在面向对象系统的开发和设计过程中,经常会遇到需求变更(Requirement Changing)的情况。通常,我们已经完成了一个设计并实现了系统原型,但客户又提出了新的需求。为了满足这些新需求,我们不得不修改已有的设计。最常见的解决方案是给已经设计好的类添加新的方法,但这会导致设计不断被打补丁,系统代码频繁重新编译,最终使得设计难以封闭,代码难以维护。

        Visitor 模式提供了一种优雅的解决方案:将更新(变更)封装到一个类中(访问操作),并由待更改类提供一个接收接口,从而实现需求变更的灵活扩展。

二、模式选择

        我们通过 Visitor 模式解决上述问题。Visitor 模式的典型结构图如下:

        Visitor 模式的核心思想是 **双分派(Double-Dispatch)技术**。C++ 语言本身支持的是单分派,但通过 Visitor 模式,我们可以实现双分派的效果。

        在 Visitor 模式中,`Accept()` 操作是一个双分派的操作。具体调用哪一个 `Accept()` 实现,取决于两个因素:

(1)Element 的类型:因为 `Accept()` 是多态操作,需要具体的 `Element` 子类来决定调用哪个 `Accept()` 实现。
(2)Visitor 的类型:`Accept()` 操作有一个参数(`Visitor* vis`),需要根据实际传入的 `Visitor` 类型来决定调用哪个 `VisitConcrete()` 实现。

三、代码实现

        下面我们将通过一个完整的 C++ 代码示例来展示如何实现 Visitor 模式。

代码片段 1:Visitor.h

// Visitor.h
#ifndef _VISITOR_H_
#define _VISITOR_H_class ConcreteElementA;
class ConcreteElementB;class Visitor {
public:virtual ~Visitor() {}virtual void VisitConcreteElementA(ConcreteElementA* elmA) = 0;virtual void VisitConcreteElementB(ConcreteElementB* elmB) = 0;
protected:Visitor() {}
};class ConcreteVisitorA : public Visitor {
public:ConcreteVisitorA() {}virtual ~ConcreteVisitorA() {}virtual void VisitConcreteElementA(ConcreteElementA* elmA);virtual void VisitConcreteElementB(ConcreteElementB* elmB);
};class ConcreteVisitorB : public Visitor {
public:ConcreteVisitorB() {}virtual ~ConcreteVisitorB() {}virtual void VisitConcreteElementA(ConcreteElementA* elmA);virtual void VisitConcreteElementB(ConcreteElementB* elmB);
};#endif //~_VISITOR_H_

代码片段 2:Visitor.cpp

// Visitor.cpp
#include "Visitor.h"
#include "Element.h"
#include <iostream>
using namespace std;Visitor::Visitor() {// 构造函数
}Visitor::~Visitor() {// 析构函数
}void ConcreteVisitorA::VisitConcreteElementA(ConcreteElementA* elmA) {cout << "ConcreteVisitorA is visiting ConcreteElementA..." << endl;
}void ConcreteVisitorA::VisitConcreteElementB(ConcreteElementB* elmB) {cout << "ConcreteVisitorA is visiting ConcreteElementB..." << endl;
}void ConcreteVisitorB::VisitConcreteElementA(ConcreteElementA* elmA) {cout << "ConcreteVisitorB is visiting ConcreteElementA..." << endl;
}void ConcreteVisitorB::VisitConcreteElementB(ConcreteElementB* elmB) {cout << "ConcreteVisitorB is visiting ConcreteElementB..." << endl;
}

代码片段 3:Element.h

// Element.h
#ifndef _ELEMENT_H_
#define _ELEMENT_H_class Visitor;class Element {
public:virtual ~Element() {}virtual void Accept(Visitor* vis) = 0;
protected:Element() {}
};class ConcreteElementA : public Element {
public:ConcreteElementA() {}virtual ~ConcreteElementA() {}virtual void Accept(Visitor* vis);
};class ConcreteElementB : public Element {
public:ConcreteElementB() {}virtual ~ConcreteElementB() {}virtual void Accept(Visitor* vis);
};#endif //~_ELEMENT_H_

代码片段 4:Element.cpp

// Element.cpp
#include "Element.h"
#include "Visitor.h"
#include <iostream>
using namespace std;Element::Element() {// 构造函数
}Element::~Element() {// 析构函数
}void ConcreteElementA::Accept(Visitor* vis) {vis->VisitConcreteElementA(this);cout << "ConcreteElementA is being visited..." << endl;
}void ConcreteElementB::Accept(Visitor* vis) {vis->VisitConcreteElementB(this);cout << "ConcreteElementB is being visited..." << endl;
}

代码片段 5:main.cpp

// main.cpp
#include "Element.h"
#include "Visitor.h"
#include <iostream>
using namespace std;int main(int argc, char* argv[]) {Visitor* visA = new ConcreteVisitorA();Visitor* visB = new ConcreteVisitorB();Element* elmA = new ConcreteElementA();Element* elmB = new ConcreteElementB();elmA->Accept(visA); // ConcreteVisitorA 访问 ConcreteElementAelmB->Accept(visA); // ConcreteVisitorA 访问 ConcreteElementBelmA->Accept(visB); // ConcreteVisitorB 访问 ConcreteElementAelmB->Accept(visB); // ConcreteVisitorB 访问 ConcreteElementBdelete visA;delete visB;delete elmA;delete elmB;return 0;
}

代码说明

在实现 Visitor 模式时,需要注意以下几点:

(1)Visitor 类中的 Visit() 操作:
        可以为 Element 类提供一个统一的 Visit() 接口,在 Accept() 中通过函数重载(overload)的方式调用具体的 Visit() 实现。
        在 C++ 中,还可以通过 RTTI(运行时类型识别)来实现,即提供一个统一的 Visit() 函数体,传入 Element* 类型的参数,然后在函数内部通过 RTTI 决定具体的 ConcreteElement 类型,并执行相应的操作。

(2)双分派机制:
        双分派机制使得 Accept() 操作能够根据 Element 和 Visitor 的类型动态决定调用哪个具体的 Visit() 实现。

四、总结讨论

        Visitor 模式可以在不修改 `Element` 类的情况下为其增加新的操作,但这也带来了一些问题:

(1)破坏了封装性:
        Visitor 模式要求 Visitor 可以从外部修改 Element 对象的状态,这通常通过以下两种方式实现:
        Element 提供足够的 public 接口,使得 Visitor 可以通过调用这些接口修改 Element 的状态。
        Element 暴露更多的细节给 Visitor,或者将 Visitor 声明为 Element 的 friend 类。这种方式会破坏封装性原则。

(2)扩展困难:
        每增加一个 Element 的子类,就需要修改 Visitor 的接口,以提供对新子类的访问机制。无论是增加新的 Visit() 接口,还是修改 RTTI 实现,都会增加扩展的复杂性。

        Visitor 模式是一种强大的设计模式,特别适用于需要在不修改现有类的情况下为其增加新操作的场景。然而,使用 Visitor 模式时需要权衡其带来的灵活性和对封装性的破坏,以及扩展时的复杂性。在实际开发中,应根据具体需求谨慎选择是否使用 Visitor 模式。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词