• 21 C++设计模式之访问者(Visitor)模式


    访问者(Visitor)模式定义

    访问者模式(Visitor),表示一个作用于某对象结构中的各个元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作

    访问者(Visitor)模式优缺点

    优点
    • 开闭原则。你可以引入在不同类对象上执行的新行为,且无需对这些类做出修改。
    • 单一职责原则。可将同一行为的不同版本移到同一个类中。
    • 访问者对象可以在与各种对象交互时收集一些有用的信息。当你想要遍历一些复杂的对象结构(例如对象树),并在结构中的每个对象上应用访问者时,这些信息可能会有所帮助。
    缺点
    • 每次在元素层次结构中添加或移除一个类时,你都要更新所有的访问者。
    • 在访问者同某个元素进行交互时,它们可能没有访问元素私有成员变量和方法的必要权限。

    访问者(Visitor)模式构成与实现

    构成
    • 访问者(Visitor)接口声明了一系列以对象结构的具体元素为参数的访问者方法。如果编程语言支持重载,这些方法的名称可以是相同的,但是其参数一定是不同的。
    • 具体访问者(Concrete Visitor)会为不同的具体元素类实现相同行为的几个不同版本。
    • 元素(Element) 接口声明了一个方法来“接收” 访问者。该方法必须有一个参数被声明为访问者接口类型。
    • 具体元素(Concrete Element)必须实现接收方法。 该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。请注意,即使元素基类实现了该方法,所有子类都必须对其进行重写并调用访问者对象中的合适方法。
    • 客户端(Client)通常会作为集合或其他复杂对象(例如一个组合树)的代表。 客户端通常不知晓所有的具体元素类,因为它们会通过抽象接口与集合中的对象进行交互。
    实例

    Visitor.h:

    #ifndef VISTOR_H_
    #define VISTOR_H_
    
    #include 
    
    class Apple;
    class Book;
    
    // 抽象访问者
    class Vistor {
     public:
        void set_name(std::string name) {
            name_ = name;
        }
    
        virtual void visit(Apple *apple) = 0;
        virtual void visit(Book *book) = 0;
    
     protected:
        std::string name_;
    };
    
    #endif  // VISTOR_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Element.h:

    #ifndef ELEMENT_H_
    #define ELEMENT_H_
    
    #include "Visitor.h"
    
    // 抽象元素类
    class Product {
     public:
        virtual void accept(Vistor *vistor) = 0;
    };
    
    #endif  // ELEMENT_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ConcreteVisitor.h:

    #ifndef CONCRETE_VISTOR_H_
    #define CONCRETE_VISTOR_H_
    
    #include 
    #include "Visitor.h"
    
    // 具体访问者类: 顾客
    class Customer : public Vistor {
     public:
        void visit(Apple *apple) {
            std::cout << "顾客" << name_ << "挑选苹果。" << std::endl;
        }
    
        void visit(Book *book) {
            std::cout << "顾客" << name_ << "买书。" << std::endl;
        }
    };
    
    // 具体访问者类: 收银员
    class Saler : public Vistor {
     public:
        void visit(Apple *apple) {
            std::cout << "收银员" << name_ << "给苹果过称, 然后计算价格。" << std::endl;
        }
    
        void visit(Book *book) {
            std::cout << "收银员" << name_ << "计算书的价格。" << std::endl;
        }
    };
    
    #endif  // CONCRETE_VISTOR_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    ConcreteElement.h:

    #ifndef CONCRETE_ELEMENT_H_
    #define CONCRETE_ELEMENT_H_
    
    #include "Element.h"
    
    // 具体产品类: 苹果
    class Apple : public Product {
     public:
        void accept(Vistor *vistor) override {
            vistor->visit(this);
        }
    };
    
    // 具体产品类: 书籍
    class Book : public Product {
     public:
        void accept(Vistor *vistor) override {
            vistor->visit(this);
        }
    };
    
    
    #endif  // CONCRETE_ELEMENT_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Client.h:

    #ifndef CLIENT_H_
    #define CLIENT_H_
    
    #include 
    #include "Visitor.h"
    #include "Element.h"
    
    // 购物车
    class ShoppingCart {
     public:
        void accept(Vistor *vistor) {
            for (auto prd : prd_list_) {
                prd->accept(vistor);
            }
        }
    
        void addProduct(Product *product) {
            prd_list_.push_back(product);
        }
    
        void removeProduct(Product *product) {
            prd_list_.remove(product);
        }
    
     private:
        std::list<Product*> prd_list_;
    };
    
    #endif  // CLIENT_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    main.cpp:

    #include "Client.h"
    #include "ConcreteElement.h"
    #include "ConcreteVisitor.h"
    
    int main() {
    
        system("chcp 65001");
        Book book;
        Apple apple;
        ShoppingCart basket;
    
        basket.addProduct(&book);
        basket.addProduct(&apple);
    
        Customer customer;
        customer.set_name("小张");
        basket.accept(&customer);
    
        Saler saler;
        saler.set_name("小杨");
        basket.accept(&saler);
        system("pause");
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    输出:

    Active code page: 65001
    顾客小张买书。
    顾客小张挑选苹果。
    收银员小杨计算书的价格。
    收银员小杨给苹果过称, 然后计算价格。
    Press any key to continue . . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    11:日志分析系统ELK|Elasticsearch|kibana
    lammps教程:原子平动和振动的设置方法
    HashSet和LinkedHashSet
    java打印语句:sex8.rar670
    【iOS】—— 六大原则和工厂模式
    PostGreSQL:时间戳时区问题
    ORB-SLAM2 ---- Tracking::MonocularInitialization函数
    linux使用操作[1]
    Java I/O在Android中应用
    SpringCloud之Seata基本介绍与安装
  • 原文地址:https://blog.csdn.net/qq_45531502/article/details/126419553