设计模式(C++)-行为型模式-访问者模式

设计模式(C++)-行为型模式-访问者模式

一、访问者模式概述

访问者模式是一种行为型设计模式,允许在不修改已有类结构的情况下定义新的操作。它将算法与对象结构分离,使得可以在不改变各元素类的前提下定义作用于这些元素的新操作。
核心思想:双重分派:1. 元素对象接受访问者;2.访问者访问具体元素

  • 主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
  • 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
  • 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),

二、访问者模式UML类图

访问者模式场景

假设我们有一个简单的文档编辑器,它包含不同类型的元素,如Paragraph和Header。我们希望添加一个功能,可以访问这些元素并执行某些操作。在这个示例中,我们定义了一个Visitor接口,它有两个纯虚函数visit,分别用于访Paragraph和Header对象。ConcreteVisitor类实现了Visitor接口,并为每种元素提供了具体的操作。Element是一个抽象基类,它定义了一个accept方法,这个方法接受一个Visitor对象。Paragraph和Header类继承自Element,并实现了accept方法,该方法调用访问者的visit方法。Document类是一个对象结构,它包含一个元素列表,并且有一个accept方法,该方法

遍历所有元素并调用它们的accept方法。最后,在main函数中,我们创建了一个Document对象,添加了一些元素,并使用ConcreteVisitor来访问这些元素。这个示例展示了如何在C++中使用Visitor模式来对一个对象结构中的元素执行不同的操作,而不需要修改这些元素的类。

三、代码实现

cpp 复制代码
//visitor.h
#pragma once
/*
Visitor 模式(访问者模式)
- 访问者模式(VisitorPattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变
数据结构的前提下定义作用于这些元素的新的操作。
- 主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
- 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
- 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),
同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
特别适用于当你需要对一个对象集合执行多种不同的操作,而又不想在每个对象类中都添加这些操作时。
核心概念:
Visitor(访问者):定义了一个访问者接口,声明了对每一个元素类访问的方法。
ConcreteVisitor(具体访问者):实现Visitor接口,为每一个元素类提供相应的处理。
Element(元素):定义了一个接受访问者的接口。
ConcreteElement(具体元素):实现Element接口,为访问者提供接收访问的方法。
ObjectStructure(对象结构):是一个包含元素的容器,可以遍历元素,允许访问者访问每一个元素。
应用场景:
当一个对象结构中的对象需要执行多种不同的操作,而这些操作可能在未来会不断增加时。
当这些操作与对象结构中的对象有紧密的耦合关系,但又能独立于对象结构变化时
示例:
假设我们有一个简单的文档编辑器,它包含不同类型的元素,如Paragraph和Header。我们希望添加一个功能,可以访问这些元素并执行某些操作。

在这个示例中,我们定义了一个Visitor接口,它有两个纯虚函数visit,分别用于访问Paragraph和Header对象。ConcreteVisitor类实现了Visitor接口,
并为每种元素提供了具体的操作。Element是一个抽象基类,它定义了一个accept方法,这个方法接受一个Visitor对象。Paragraph和Header类继承自
Element,并实现了accept方法,该方法调用访问者的visit方法。Document类是一个对象结构,它包含一个元素列表,并且有一个accept方法,该方法
遍历所有元素并调用它们的accept方法。最后,在main函数中,我们创建了一个Document对象,添加了一些元素,并使用ConcreteVisitor来访问这些
元素。这个示例展示了如何在C++中使用Visitor模式来对一个对象结构中的元素执行不同的操作,而不需要修改这些元素的类。
*/
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Paragraph;
class Header;
class Visitor {
public:
	~Visitor() {};
	virtual void visit(Paragraph*p) = 0;
	virtual void visit(Header*h) = 0;
};
//定义具体的访问者
class ConcreteVisitor :public Visitor {
public:
	void visit(Paragraph*p);
	void visit(Header*h);
};

//定义元素接口
class Element {
public:
	virtual ~Element() {};
	virtual void accept(Visitor*v) = 0;
};
class Paragraph :public Element {
public:
	Paragraph(const string&content) :m_content(content) {};
	void accept(Visitor*v);
	string getContent()const;
private:
	string m_content;
};
class Header :public Element {
public:
	Header(const string&content) :m_content(content) {};
	void accept(Visitor*v);
	string getContent()const;
private:
	string m_content;
};
//定义对象结构
class Document{
public:
	void addElement(std::unique_ptr<Element>elem);
	void accept(Visitor*v);
private:
	vector<std::unique_ptr<Element> > m_elements;
};
void testVisitor();
//visitor.cc
#include "visitor.h"
void ConcreteVisitor::visit(Paragraph*p) {
	cout << "Visiting a paragraph with content: " << p->getContent().c_str() << endl;
}
void ConcreteVisitor::visit(Header*h) {
	cout << "Visiting a Header with content: " << h->getContent().c_str() << endl;
}
void Paragraph::accept(Visitor*v) {
	v->visit(this);
}
string Paragraph::getContent()const {
	return m_content;
}
void Header::accept(Visitor*v) {
	v->visit(this);
}
string Header::getContent()const {
	return m_content;
}
void Document::addElement(std::unique_ptr<Element>elem) {
	m_elements.emplace_back(elem.release());
}
void Document::accept(Visitor*v) {
	for (auto &elem:m_elements)
	{
		elem->accept(v);
	}
}
void testVisitor(){
	cout << "=================visitor start===============" << endl;
	Document doc;
	doc.addElement(std::make_unique<Paragraph>("This is a paragraph."));
	doc.addElement(std::make_unique<Header>("This is a header."));

	ConcreteVisitor visitor;
	doc.accept(&visitor);
	cout << "=================visitor end===============" << endl;
}

四、优缺点总结

优点:

  • 开闭原则:新增操作容易,无需修改元素类
  • 单一职责原则:相关行为集中在访问者中
  • 跨元素操作:可以在一个访问者中实现跨不同元素的操作
  • 算法集中:相关算法集中在一个类中
  • 状态累积:访问者可以在访问过程中积累状态

缺点:

  • 破坏封装:访问者需要公开元素内部细节
  • 增加新元素困难:每增加新元素,需要修改所有的访问者
  • 难以访问私有成员:需要使用友元或公开方法
  • 元素依赖访问者:元素必须了解访问者接口
相关推荐
血玥珏2 小时前
血玥珏-多WAV/MP3混音合成小工具2.0.0.5
c++·音视频
workflower2 小时前
农业信息化
大数据·人工智能·设计模式·机器人·软件工程
Shadow(⊙o⊙)2 小时前
初识Qt+经典方式实现hello world!的交互
开发语言·c++·后端·qt·学习
梵尔纳多2 小时前
OpenGL 实例化
c++·图形渲染·opengl
隐士Xbox2 小时前
c++ 指针的用法
开发语言·c++·计算机视觉
salipopl3 小时前
C++ 面试题:C++中 constexpr 函数的限制有哪些?
c++
无限进步_3 小时前
【C++】从红黑树到 map 和 set:封装设计与迭代器实现
开发语言·数据结构·数据库·c++·windows·github·visual studio
handler013 小时前
速通蓝桥杯省一:二分算法
c语言·开发语言·c++·笔记·算法·职场和发展·蓝桥杯
Pkmer3 小时前
工厂方法Factory Method
设计模式·langchain