C++设计模式之访问者模式

【声明】本题目来源于卡码网(卡码网KamaCoder
【提示:如果不想看文字介绍,可以直接跳转到C++编码部分


设计模式大纲】

【简介】

--什么是访问者模式(第23种设计模式,至此设计模式更新结束!)

访问者模式 (Visitor Pattern)是⼀种行为型设计模式 ,可以在不改变对象结构 的前提下,对对象中的元素进行新的操作

举个例子,假设有⼀个动物园,里面有不同种类的动物,比如狮子、⼤象、猴子等。每个动物都会被医生检查身体,被管理员投喂,被游客观看,医生,游客,管理员都属于访问者。

【Java代码示例】

java 复制代码
// 定义动物接⼝
interface Animal {
    void accept(Visitor visitor);
}
// 具体元素类:狮⼦
class Lion implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
// 具体元素类:⼤象
class Elephant implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
// 具体元素类:猴⼦
class Monkey implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

如果你想对动物园中的每个动物执行⼀些操作,比如医⽣健康检查、管理员喂⻝、游客观赏等。就可以使⽤访问者模式来实现这些操作。

java 复制代码
// 定义访问者接⼝
interface Visitor {
    void visit(Animal animal);
}
// 具体访问者类:医⽣
class Vet implements Visitor {
    @Override
    public void visit(Animal animal) {
        // 操作
    }
}
// 具体访问者类:管理员
class Zookeeper implements Visitor {
    @Override
    public void visit(Animal animal) {
        // 操作
    }
}
// 具体访问者类:游客
class VisitorPerson implements Visitor {
    @Override
    public void visit(Animal animal) {
        // 操作
    }
}

将这些访问者应⽤到动物园的每个动物上:

java 复制代码
/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file VisitorMode.hpp
* @brief 访问者模式
* @autor 写代码的小恐龙er
* @date 2024/01/26
*/
public class Main {
    public static void main(String[] args) {
        Animal lion = new Lion();
        Animal elephant = new Elephant();
        Animal monkey = new Monkey();

        Visitor vet = new Vet();
        Visitor zookeeper = new Zookeeper();
        Visitor visitorPerson = new VisitorPerson();

        // 动物接受访问者的访问
        lion.accept(vet);
        elephant.accept(zookeeper);
        monkey.accept(visitorPerson);
    }
}

【基本结构】

访问者模式包括以下几个基本角色:

  • 抽象访问者(Visitor): 声明了访问者可以访问哪些元素,以及如何访问它们的⽅法visit 。
  • 具体访问者(ConcreteVisitor): 实现了抽象访问者定义的⽅法,不同的元素类型可能有不同的访问⾏为。医⽣、管理员、游客都属于具体的访问者,它们的访问⾏为不同。
  • 抽象元素(Element) : 定义了⼀个accept⽅法 ,用于接受访问者的访问
  • 具体元素(ConcreteElement)实现了accept方法 ,是访问者访问的⽬标
  • 对象结构(Object Structure) : 包含元素的集合,可以是⼀个列表、一个集合或者其他数据结构。负责遍历元素,并调用元素的接受⽅法。

【简易实现 - Java】

1. 定义抽象访问者

声明哪些元素可以访问。

java 复制代码
// 抽象访问者
interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}

2. 实现具体访问者

实现具体的访问逻辑。

java 复制代码
// 具体访问者A
class ConcreteVisitorA implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("ConcreteVisitorA Visit ConcreteElementA");
    }
    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("ConcreteVisitorA Visit ConcreteElementB");
    }
}
// 具体访问者B
class ConcreteVisitorB implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("ConcreteVisitorB Visit ConcreteElementA");
    }
    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("ConcreteVisitorB Visit ConcreteElementB");
    }
}

3. 定义元素接口

声明接收访问者的⽅法。

java 复制代码
// 抽象元素
interface Element {
    void accept(Visitor visitor);
}

4. 实现具体元素

实现接受访问者的方法。

java 复制代码
// 具体元素A
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
// 具体元素B
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

5. 创建对象结构

提供⼀个接口让访问者访问它的元素。

java 复制代码
// 对象结构
class ObjectStructure {
    private List<Element> elements = new ArrayList<>();
    public void attach(Element element) {
        elements.add(element);
    }
    public void detach(Element element) {
        elements.remove(element);
    }
    public void accept(Visitor visitor) {
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

6. 客户端调用

java 复制代码
/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file VisitorMode.hpp
* @brief 访问者模式
* @autor 写代码的小恐龙er
* @date 2024/01/26
*/
public class Main {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.attach(new ConcreteElementA());
        objectStructure.attach(new ConcreteElementB());

        Visitor visitorA = new ConcreteVisitorA();
        Visitor visitorB = new ConcreteVisitorB();

        objectStructure.accept(visitorA);
        objectStructure.accept(visitorB);
    }
}

【使用场景】

访问者模式结构较为复杂,但是访问者模式将同⼀类操作封装在⼀个访问者中,使得相关的操作彼此集中,提⾼了代码的可读性和维护性。它常用于对象结构⽐较稳定 ,但经常需要在此对象结构上定义新的操作,这样就无需修改现有的元素类,只需要定义新的访问者来添加新的操作。


【C++编码部分】

1. 题目描述

小明家有一些圆形和长方形面积的土地,请你帮他实现一个访问者模式,使得可以通过访问者计算每块土地的面积。

图形的面积计算规则如下:

  • 圆形的面积计算公式为:3.14 * 半径 * 半径
  • 矩形的面积计算公式为:长 * 宽

2. 输入描述

第一行是一个整数 n(1 <= n <= 1000),表示图形的数量。 接下来的 n 行,每行描述一个图形,格式为 "Circle r" 或 "Rectangle width height",其中 r、width、height 是正整数。

3. 输出描述

对于每个图形,输出一行,表示该图形的面积。

4. C++编程实例

cpp 复制代码
/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file VisitorMode.hpp
* @brief 访问者模式
* @autor 写代码的小恐龙er
* @date 2024/01/26
*/

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// 前置声明

// 元素接口 【定义了⼀个accept⽅法,⽤于接受访问者的访问】
class Shape;
// 具体元素 -- 圆形 
class Circle;
// 具体元素 -- 矩形
class Rectangle;

//  访问者接口类 -- 【声明了访问者可以访问哪些元素,以及如何访问它们的⽅法visit】
class Visitor;
// 具体访问者 -- 计算面积
class AreaCalculator;

// 对象结构类 -- 【包含元素的集合,可以是⼀个列表、⼀个集合或者其他数据结构。
// 负责遍历元素,并调⽤元素的接受⽅法。】
class Drawing;

// 类的定义

//  访问者接口类 -- 【声明了访问者可以访问哪些元素,以及如何访问它们的⽅法visit】
class Visitor
{
public:
    virtual void VisitElem(Circle *circle) = 0;
    virtual void VisitElem(Rectangle *rectangle) = 0;
};

class Shape
{
public:
    // ⽤于接受访问者的访问
    virtual void AcceptVisitor(Visitor *visitor) = 0;
};

// 具体元素 -- 圆形 
class Circle : public Shape
{
private:
    // 半径
    int _radius = 0;
public:
    // 构造函数
    Circle(int radius){
        this->_radius = radius;
    }
    // 成员数据接口
    void SetRadius(int radius){
        this->_radius = radius;
    }
    int GetRadius(){
        return this->_radius;
    }
    // 重载 基类接口函数
    void AcceptVisitor(Visitor *visitor) override{
        visitor->VisitElem(this);
    }
};

// 具体元素 -- 矩形 
class Rectangle : public Shape
{
private:
    // 宽
    int _width = 0;
    // 高
    int _height = 0;
    
public:
    // 构造函数
    Rectangle(int width, int height){
        this->_width = width;
        this->_height = height;
    }
    // 成员数据接口
    void SetWidth(int width){
        this->_width = width;
    }
    int GetWidth(){
        return this->_width;
    }
    void SetHeight(int height){
        this->_height = height;
    }
    int GetHeight(){
        return this->_height;
    }
    // 重载 基类接口函数
    void AcceptVisitor(Visitor *visitor) override{
        visitor->VisitElem(this);
    }
};

// 具体访问者 -- 计算面积
class AreaCalculator : public Visitor
{
// 重载两个接口函数
public:
    void VisitElem(Circle *circle) override{
        double area = 3.14 * circle->GetRadius() * circle->GetRadius();
        std::cout << area << endl;
    }
    void VisitElem(Rectangle *rectangle) override{
        int area = rectangle->GetWidth() * rectangle->GetHeight();
        std::cout << area << endl;
    }
};

// 对象结构类
class Drawing
{
// 存放所有的元素 Element
private:
    vector<Shape *> _elementVec;
public:
    // 构造函数
    Drawing(){_elementVec.clear();}
    // 添加元素
    void AddElement(Shape * shape){
        _elementVec.push_back(shape);
    }
    // 移除元素
    void RemoveElement(Shape * shape){
        for(Shape * it : _elementVec){
            if(it == shape){
                it = nullptr;
            }
        }
    }
    // 移除尾部元素
    void PopElement(){
        _elementVec.pop_back();
    }
    
public:
    // 将所有的元素一起访问
    void AcceptAllElement(Visitor *visitor){
        for(Shape * it : _elementVec){
            it->AcceptVisitor(visitor);
        }
    }
};

int main(){
    // 图形数量
    int shapeNum = 0;
    std::cin >> shapeNum;
    // 构造图形元素基类
    Shape *shape = nullptr;
    // 构造具体访问者
    Visitor *visitor= new AreaCalculator();
    // 创建对象结构类
    Drawing *drawing = new Drawing();
    // 遍历 
    for(int i = 0; i < shapeNum; i++){
        string shapeType = "";
        int radius = 0;
        int width = 0;
        int height = 0;
        std::cin >> shapeType;
        if(shapeType == "Circle"){
            std::cin >> radius;
            // 创建圆形
            shape = new Circle(radius);
            // 添加进入管理类
            drawing->AddElement(shape);
        }
        else if(shapeType == "Rectangle"){
            std:: cin >> width >> height;
            // 构建矩形类
            shape = new Rectangle(width, height);
            // 添加进入管理类
            drawing->AddElement(shape);
        }
        else continue;
    }
    
    // 开始打印信息 -- 访问者执行访问操作
    drawing->AcceptAllElement(visitor);
    
    // 析构!
    if(shape != nullptr){
        delete shape;
        shape = nullptr;
    }

    delete drawing;
    drawing = nullptr;
    
        
    delete visitor;
    visitor = nullptr;
    return 0;
}


至此,所有的设计模式通过Java代码的简易实现说明与C++具体实例编程的方式为大家简明扼要的做出了介绍,希望这些博客对大家在软件开发等相关工作中提供一定的帮助!

......

To be continued.

相关推荐
CocoaAndYy22 分钟前
设计模式-单例模式
单例模式·设计模式
程序员南飞1 小时前
ps aux | grep smart_webrtc这条指令代表什么意思
java·linux·ubuntu·webrtc
StrokeAce1 小时前
linux桌面软件(wps)内嵌到主窗口后的关闭问题
linux·c++·qt·wps·窗口内嵌
弥琉撒到我1 小时前
微服务swagger解析部署使用全流程
java·微服务·架构·swagger
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
问道飞鱼2 小时前
Java基础-单例模式的实现
java·开发语言·单例模式
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
家有狸花4 小时前
VSCODE驯服日记(三):配置C++环境
c++·ide·vscode
dengqingrui1235 小时前
【树形DP】AT_dp_p Independent Set 题解
c++·学习·算法·深度优先·图论·dp
C++忠实粉丝5 小时前
前缀和(8)_矩阵区域和
数据结构·c++·线性代数·算法·矩阵