《设计模式的艺术》笔记 - 访问者模式

介绍

访问者模式提供一个作用于某对象结构中的各元素的操作表示,它使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。

实现

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Visitor;

class Element { // 抽象元素类
public:
    virtual void accept(Visitor *visitor) = 0;
};

class ConcreteElementA : public Element {
public:
    void accept(Visitor *visitor) override;
};

class ConcreteElementB : public Element {
public:
    void accept(Visitor *visitor) override;
};

class Visitor {    // 抽象访问类
public:
    virtual void visit(ConcreteElementA *elementA) = 0;
    virtual void visit(ConcreteElementB *elementB) = 0;
};

class ConcreteVisitor : public Visitor {
public:
    void visit(ConcreteElementA *elementA) override;

    void visit(ConcreteElementB *elementB) override;
};

class ObjectStructure {
public:
    ~ObjectStructure();
    void addElement(Element *ele);
    void removeElement(Element *ele);
    void accept(Visitor *visitor);
private:
    std::vector<Element *> m_eles;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>

void ConcreteElementA::accept(Visitor *visitor) {
    std::cout << "A对象接受访问" << std::endl;
    visitor->visit(this);
}

void ConcreteElementB::accept(Visitor *visitor) {
    std::cout << "B对象接受访问" << std::endl;
    visitor->visit(this);
}

void ConcreteVisitor::visit(ConcreteElementA *elementA) {
    std::cout << "ConcreteVisitor访问了A对象" << std::endl;
}

void ConcreteVisitor::visit(ConcreteElementB *elementB) {
    std::cout << "ConcreteVisitor访问了B对象" << std::endl;
}

ObjectStructure::~ObjectStructure() {
    for (auto &e : m_eles) {
        if (e) {
            delete e;
        }
    }
}

void ObjectStructure::addElement(Element *ele) {
    m_eles.push_back(ele);
}

void ObjectStructure::removeElement(Element *ele) {
    for (auto it = m_eles.begin(); it != m_eles.end(); ++it) {
        if (*it == ele) {
            m_eles.erase(it);
            break;
        }
    }
}

void ObjectStructure::accept(Visitor *visitor) {
    for (auto &e : m_eles) {
        e->accept(visitor);
    }
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    ObjectStructure *objs = new ObjectStructure;
    objs->addElement(new ConcreteElementA);
    objs->addElement(new ConcreteElementB);
    Visitor *visitor = new ConcreteVisitor;
    objs->accept(visitor);

    delete objs;
    delete visitor;

    return 0;
}

总结

优点

  1. 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合开闭原则。

  2. 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。

  3. 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。

缺点

  1. 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了开闭原则的要求。

  2. 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。

适用场景

  1. 一个对象结构包含多种类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。

  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而且需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。访问者模式将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。

  3. 对象结构中元素对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

练习

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Visitor;

class Element { // 抽象元素类
public:
    Element(const std::string &name);
    virtual void accept(Visitor *visitor) = 0;

    uint32_t getMThesisNum() const;

    uint32_t getMScore() const;

    void setMThesisNum(uint32_t mThesisNum);

    void setMScore(uint32_t mScore);

    const std::string &getMName() const;

private:
    uint32_t m_thesisNum;
    uint32_t m_score;
    std::string m_name;
};

class Student : public Element {
public:
    Student(const std::string &name, uint32_t thesisNum, uint32_t score);
    void accept(Visitor *visitor) override;
};

class Teacher : public Element {
public:
    Teacher(const std::string &name, uint32_t thesisNum, uint32_t score);
    void accept(Visitor *visitor) override;
};

class Visitor {    // 抽象访问类
public:
    virtual void selection(Student *stu) = 0;
    virtual void selection(Teacher *tea) = 0;
};

class ScientificVisitor : public Visitor {
public:
    void selection(Student *stu) override;

    void selection(Teacher *tea) override;
};

class ExcellentVisitor : public Visitor {
public:
    void selection(Student *stu) override;

    void selection(Teacher *tea) override;
};

class ObjectStructure {
public:
    ~ObjectStructure();
    void addElement(Element *ele);
    void removeElement(Element *ele);
    void accept(Visitor *visitor);
private:
    std::vector<Element *> m_eles;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>

Element::Element(const std::string &name) {
    m_name = name;
}

uint32_t Element::getMThesisNum() const {
    return m_thesisNum;
}

uint32_t Element::getMScore() const {
    return m_score;
}

void Element::setMThesisNum(uint32_t mThesisNum) {
    m_thesisNum = mThesisNum;
}

void Element::setMScore(uint32_t mScore) {
    m_score = mScore;
}

const std::string &Element::getMName() const {
    return m_name;
}

Student::Student(const std::string &name, uint32_t thesisNum, uint32_t score) : Element(name) {
    setMThesisNum(thesisNum);
    setMScore(score);
}

void Student::accept(Visitor *visitor) {
    visitor->selection(this);
}

Teacher::Teacher(const std::string &name, uint32_t thesisNum, uint32_t score) : Element(name) {
    setMThesisNum(thesisNum);
    setMScore(score);
}

void Teacher::accept(Visitor *visitor) {
    visitor->selection(this);
}

ObjectStructure::~ObjectStructure() {
    for (auto &e : m_eles) {
        if (e) {
            delete e;
        }
    }
}

void ObjectStructure::addElement(Element *ele) {
    m_eles.push_back(ele);
}

void ObjectStructure::removeElement(Element *ele) {
    for (auto it = m_eles.begin(); it != m_eles.end(); ++it) {
        if (*it == ele) {
            m_eles.erase(it);
            break;
        }
    }
}

void ObjectStructure::accept(Visitor *visitor) {
    for (auto &e : m_eles) {
        e->accept(visitor);
    }
}


void ScientificVisitor::selection(Student *stu) {
    if (stu->getMThesisNum() >= 2) {
        std::cout << stu->getMName() << "同学可以被评为科研奖" << std::endl;
    } else {
        std::cout << stu->getMName() << "同学不能被评为科研奖" << std::endl;
    }
}

void ScientificVisitor::selection(Teacher *tea) {
    if (tea->getMThesisNum() >= 10) {
        std::cout << tea->getMName() << "老师可以被评为科研奖" << std::endl;
    } else {
        std::cout << tea->getMName() << "老师不能被评为科研奖" << std::endl;
    }
}

void ExcellentVisitor::selection(Student *stu) {
    if (stu->getMScore() >= 90) {
        std::cout << stu->getMName() << "同学可以被评为优秀奖" << std::endl;
    } else {
        std::cout << stu->getMName() << "同学不能被评为优秀奖" << std::endl;
    }
}

void ExcellentVisitor::selection(Teacher *tea) {
    if (tea->getMScore() >= 90) {
        std::cout << tea->getMName() << "老师可以被评为优秀奖" << std::endl;
    } else {
        std::cout << tea->getMName() << "老师不能被评为优秀奖" << std::endl;
    }
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    ObjectStructure *objs = new ObjectStructure;
    objs->addElement(new Student("小明", 2, 80));
    objs->addElement(new Teacher("老王", 8, 95));
    Visitor *visitor = new ScientificVisitor;
    objs->accept(visitor);
    delete visitor;
    visitor = new ExcellentVisitor;
    objs->accept(visitor);
    delete objs;
    delete visitor;

    return 0;
}
相关推荐
lmxnsI3 分钟前
docker使用笔记
笔记·docker·容器
lijiachang03071838 分钟前
设计模式(一):单例模式
c++·笔记·学习·程序人生·单例模式·设计模式·大学生
Suwg2091 小时前
《手写Mybatis渐进式源码实践》实践笔记(第七章 SQL执行器的创建和使用)
java·数据库·笔记·后端·sql·mybatis·模板方法模式
抓哇FullStack-Junior1 小时前
设计模式——适配器模式
java·设计模式·适配器模式
胡西风_foxww1 小时前
【ES6复习笔记】let 和 const 命令(1)
笔记·学习·es6·const·let
胡西风_foxww1 小时前
【ES6复习笔记】Spread 扩展运算符(8)
前端·笔记·es6·扩展·运算符·spread
silver6873 小时前
桥接模式详解
设计模式
红色的山茶花3 小时前
YOLOv9-0.1部分代码阅读笔记-anchor_generator.py
笔记·深度学习·yolo
m0_748250033 小时前
【STM32】F103ZET6开发板----笔记01
笔记·stm32·嵌入式硬件
1101 11013 小时前
STM32-笔记16-定时器中断点灯
笔记·stm32·单片机