c/c++设计模式---访问者模式

访问者(Visitor)模式:访问器模式,行为型模式。
    //(1)一个具体范例的逐渐演化
    //阿司匹林肠溶片:改善血液流通,预防血栓形成,血栓形成就产生阻塞,人就会直接面临危险;
    //氟伐他汀钠缓释片:降血脂。因为血脂高意味着血流慢,营养无法运输到身体各部位,还很可能引发心脑血管疾病;
    //黛力新:医生说我的心脏没有器质性病变,是神经出现了紊乱,出现的症状是因为神经的误报,因此开此药治疗植物神经功能紊乱以及身心疾病伴有的焦虑症状;
    //缴费,取药。

    //访问者模式:包含 访问者(收费人员、取药人员、营养师、健身教练),被访问者(元素)(药品单中的三种药品) 两个重要角色
    //访问:a)串门。b)查看一个东西
 
#include <iostream>
#include <list>


#ifdef _DEBUG   //只在Debug(调试)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{    
    //药品父类
    class Medicine
    {
    public:
        virtual string getMdcName() = 0; //药品名称
        virtual float getPrice() = 0; //药品总价格,单位:元

        virtual ~Medicine() {} //做父类应该有个虚析构函数
    };
    
    //药品:阿司匹林肠溶片
    class M_asplcrp :public Medicine
    {
    public:
        virtual string getMdcName()
        {
            return "阿司匹林肠溶片";
        }
        virtual float getPrice()
        {
            return 46.8f; //药品总价
        }
    };

    //药品:氟伐他汀钠缓释片
    class M_fftdnhsp :public Medicine
    {
    public:
        virtual string getMdcName()
        {
            return "氟伐他汀钠缓释片";
        }
        virtual float getPrice()
        {
            return 111.3f; //药品总价
        }
    };

    //药品:黛力新
    class M_dlx :public Medicine
    {
    public:
        virtual string getMdcName()
        {
            return "黛力新";
        }
        virtual float getPrice()
        {
            return 122.0f; //药品总价
        }
    };
    //----------------
    //针对药品的处理相关类
    class MedicineProc
    {
    public: 
        //增加药品到药品列表中
        void addMedicine(Medicine* p_mdc)
        {
            m_mdclist.push_back(p_mdc);
        }

        //针对费用缴纳和取药所做的处理动作
        void procAction(string strvisitor) //strvisitor代表拿到了药品单的人,不同的人拿到药品单所要做的处理不同
        {
            if (strvisitor == "收费人员") //收费人员要根据药品单向我(患者)收取费用
            {
                float totalcost = 0.0f; //总费用
                for (auto iter = m_mdclist.begin(); iter != m_mdclist.end(); ++iter)
                {
                    float tmpprice = (*iter)->getPrice();
                    cout << "收费人员累计药品\"" << (*iter)->getMdcName() << "\"的价格:" << tmpprice << endl;
                    totalcost += tmpprice;
                }//end for
                cout << "所有药品的总价为:" << totalcost << ",收费人员收取了我的费用!" << endl;
            }
            else if (strvisitor == "取药人员") //取药人员要根据药品单为我拿药
            {
                for (auto iter = m_mdclist.begin(); iter != m_mdclist.end(); ++iter)
                {
                    cout << "取药人员将药品\"" << (*iter)->getMdcName() << "\"拿给了我!" << endl;
                }
            }
            else if (strvisitor == "营养师")
            {
                cout << "营养师建议:主食中多搭配粗粮,适当食用肉类!" << endl;
            }
            else if (strvisitor == "健身教练")
            {
                cout << "健身教练建议:多做有氧运动比如慢跑(35分钟即可),慢跑前后要热身,忌焦虑,忌熬夜,以22:30之前卧床休息为宜!" << endl;
            }
        }

    private:
        list <Medicine*> m_mdclist; //药品列表,记录着药品单上的所有药品。
    };

    //-------------------
    //访问者父类
    class Visitor
    {
    public:
        virtual ~Visitor() {} //做父类时析构函数应该为虚函数

        virtual void Visit_elm_asplcrp(M_asplcrp* pelem) = 0; //访问元素:阿司匹林肠溶片
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem) = 0; //访问元素:氟伐他汀钠缓释片
        virtual void Visit_elm_dlx(M_dlx* pelem) = 0; //访问元素:黛力新

        //virtual void Visit(M_asplcrp* pelem) = 0; //访问元素:阿司匹林肠溶片
        //virtual void Visit(M_fftdnhsp* pelem) = 0; //访问元素:氟伐他汀钠缓释片
        //virtual void Visit(M_dlx* pelem) = 0; //访问元素:黛力新
    };

    //收费人员访问者子类
    class Visitor_SFRY : public Visitor
    {
        virtual void Visit_elm_asplcrp(M_asplcrp* pelem)
        {
            float tmpprice = pelem->getPrice();
            cout << "收费人员累计药品\"" << pelem->getMdcName() << "\"的价格:" << tmpprice << endl;
            m_totalcost += tmpprice;
        }
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem)
        {
            float tmpprice = pelem->getPrice();
            cout << "收费人员累计药品\"" << pelem->getMdcName() << "\"的价格:" << tmpprice << endl;
            m_totalcost += tmpprice;
        }
        virtual void Visit_elm_dlx(M_dlx* pelem)
        {
            float tmpprice = pelem->getPrice();
            cout << "收费人员累计药品\"" << pelem->getMdcName() << "\"的价格:" << tmpprice << endl;
            m_totalcost += tmpprice;
        }

        //返回总费用
        float getTotalCost()
        {
            return m_totalcost;
        }

    private:
        float m_totalcost = 0.0f; //总费用
    };

    //取药人员访问者子类
    class Visitor_QYRY : public Visitor
    {
        virtual void Visit_elm_asplcrp(M_asplcrp* pelem)
        {
            cout << "取药人员将药品\"" << pelem->getMdcName() << "\"拿给了我!" << endl;
        }
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem)
        {
            cout << "取药人员将药品\"" << pelem->getMdcName() << "\"拿给了我!" << endl;
        }
        virtual void Visit_elm_dlx(M_dlx* pelem)
        {
            cout << "取药人员将药品\"" << pelem->getMdcName() << "\"拿给了我!" << endl;
        }
    };

}

int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
    _nmsp1::Medicine* pm1 = new _nmsp1::M_asplcrp();
    _nmsp1::Medicine* pm2 = new _nmsp1::M_fftdnhsp();
    _nmsp1::Medicine* pm3 = new _nmsp1::M_dlx();

    _nmsp1::MedicineProc mdcprocobj;
    mdcprocobj.addMedicine(pm1);
    mdcprocobj.addMedicine(pm2);
    mdcprocobj.addMedicine(pm3);
    mdcprocobj.procAction("收费人员");
    mdcprocobj.procAction("取药人员");


    //释放资源
    delete pm1;
    delete pm2;
    delete pm3;




    return 0;
}
(2)引入访问者(Visitor)模式
    //定义/实现意图:提供一个作用于某对象结构中的各元素的操作表示,使可以在不改变各元素的前提下定义(扩展)作用域这些元素的新操作。
    //解释:允许一个或者多个操作应用到一组对象上,使对象本身和操作解耦。
 
#include <iostream>
#include <list>


#ifdef _DEBUG   //只在Debug(调试)模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{    
    class Visitor; //类前向声明
    //药品父类
    class Medicine
    {
    public:
        virtual string getMdcName() = 0; //药品名称
        virtual float getPrice() = 0; //药品总价格,单位:元
        virtual void Accept(Visitor* pvisitor) = 0; //这里的形参是访问和父类指针。

        virtual ~Medicine() {} //做父类应该有个虚析构函数
    };
    
    //药品:阿司匹林肠溶片
    class M_asplcrp :public Medicine
    {
    public:
        virtual string getMdcName()
        {
            return "阿司匹林肠溶片";
        }
        virtual float getPrice()
        {
            return 46.8f; //药品总价
        }
        virtual void Accept(Visitor* pvisitor);
    };

    //药品:氟伐他汀钠缓释片
    class M_fftdnhsp :public Medicine
    {
    public:
        virtual string getMdcName()
        {
            return "氟伐他汀钠缓释片";
        }
        virtual float getPrice()
        {
            return 111.3f; //药品总价
        }
        virtual void Accept(Visitor* pvisitor);
    };

    //药品:黛力新
    class M_dlx :public Medicine
    {
    public:
        virtual string getMdcName()
        {
            return "黛力新";
        }
        virtual float getPrice()
        {
            return 122.0f; //药品总价
        }
        virtual void Accept(Visitor* pvisitor);
    };
    /*
    //----------------
    //针对药品的处理相关类
    class MedicineProc
    {
    public: 
        //增加药品到药品列表中
        void addMedicine(Medicine* p_mdc)
        {
            m_mdclist.push_back(p_mdc);
        }

        //针对费用缴纳和取药所做的处理动作
        void procAction(string strvisitor) //strvisitor代表拿到了药品单的人,不同的人拿到药品单所要做的处理不同
        {
            if (strvisitor == "收费人员") //收费人员要根据药品单向我(患者)收取费用
            {
                float totalcost = 0.0f; //总费用
                for (auto iter = m_mdclist.begin(); iter != m_mdclist.end(); ++iter)
                {
                    float tmpprice = (*iter)->getPrice();
                    cout << "收费人员累计药品\"" << (*iter)->getMdcName() << "\"的价格:" << tmpprice << endl;
                    totalcost += tmpprice;
                }//end for
                cout << "所有药品的总价为:" << totalcost << ",收费人员收取了我的费用!" << endl;
            }
            else if (strvisitor == "取药人员") //取药人员要根据药品单为我拿药
            {
                for (auto iter = m_mdclist.begin(); iter != m_mdclist.end(); ++iter)
                {
                    cout << "取药人员将药品\"" << (*iter)->getMdcName() << "\"拿给了我!" << endl;
                }
            }
            else if (strvisitor == "营养师")
            {
                cout << "营养师建议:主食中多搭配粗粮,适当食用肉类!" << endl;
            }
            else if (strvisitor == "健身教练")
            {
                cout << "健身教练建议:多做有氧运动比如慢跑(35分钟即可),慢跑前后要热身,忌焦虑,忌熬夜,以22:30之前卧床休息为宜!" << endl;
            }
        }

    private:
        list <Medicine*> m_mdclist; //药品列表,记录着药品单上的所有药品。
    };
    */

    //-------------------
    //访问者父类
    class Visitor
    {
    public:
        virtual ~Visitor() {} //做父类时析构函数应该为虚函数

        virtual void Visit_elm_asplcrp(M_asplcrp* pelem) = 0; //访问元素:阿司匹林肠溶片
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem) = 0; //访问元素:氟伐他汀钠缓释片
        virtual void Visit_elm_dlx(M_dlx* pelem) = 0; //访问元素:黛力新

        //virtual void Visit(M_asplcrp* pelem) = 0; //访问元素:阿司匹林肠溶片
        //virtual void Visit(M_fftdnhsp* pelem) = 0; //访问元素:氟伐他汀钠缓释片
        //virtual void Visit(M_dlx* pelem) = 0; //访问元素:黛力新
    };

    //收费人员访问者子类
    class Visitor_SFRY : public Visitor
    {
    public:
        virtual void Visit_elm_asplcrp(M_asplcrp* pelem)
        {
            float tmpprice = pelem->getPrice();
            cout << "收费人员累计药品\"" << pelem->getMdcName() << "\"的价格:" << tmpprice << endl;
            m_totalcost += tmpprice;
        }
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem)
        {
            float tmpprice = pelem->getPrice();
            cout << "收费人员累计药品\"" << pelem->getMdcName() << "\"的价格:" << tmpprice << endl;
            m_totalcost += tmpprice;
        }
        virtual void Visit_elm_dlx(M_dlx* pelem)
        {
            float tmpprice = pelem->getPrice();
            cout << "收费人员累计药品\"" << pelem->getMdcName() << "\"的价格:" << tmpprice << endl;
            m_totalcost += tmpprice;
        }

        //返回总费用
        float getTotalCost()
        {
            return m_totalcost;
        }

    private:
        float m_totalcost = 0.0f; //总费用
    };

    //取药人员访问者子类
    class Visitor_QYRY : public Visitor
    {
        virtual void Visit_elm_asplcrp(M_asplcrp* pelem)
        {
            cout << "取药人员将药品\"" << pelem->getMdcName() << "\"拿给了我!" << endl;
        }
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem)
        {
            cout << "取药人员将药品\"" << pelem->getMdcName() << "\"拿给了我!" << endl;
        }
        virtual void Visit_elm_dlx(M_dlx* pelem)
        {
            cout << "取药人员将药品\"" << pelem->getMdcName() << "\"拿给了我!" << endl;
        }
    };

    //营养师访问者子类
    class  Visitor_YYS :public Visitor
    {
    public:
        virtual void Visit_elm_asplcrp(M_asplcrp* pelem)
        {
            cout << "营养师建议:多吃粗粮少吃油,可以有效的预防血栓!" << endl;
        }
        virtual void Visit_elm_fftdnhsp(M_fftdnhsp* pelem)
        {
            cout << "营养师建议:多吃蘑菇、洋葱、猕猴桃,可以有效的降低血脂!" << endl;
        }
        virtual void Visit_elm_dlx(M_dlx* pelem)
        {
            cout << "营养师建议:多出去走走呼吸新鲜空气,多晒太阳,多去人多热闹的地方,保持乐观开朗的心情!" << endl;
        }
    };



    //各个药品子类的Accept方法的实现体代码
    void M_asplcrp::Accept(Visitor* pvisitor)
    {
        pvisitor->Visit_elm_asplcrp(this);
    }
    void M_fftdnhsp::Accept(Visitor* pvisitor)
    {
        pvisitor->Visit_elm_fftdnhsp(this);
    }
    void M_dlx::Accept(Visitor* pvisitor)
    {
        pvisitor->Visit_elm_dlx(this);
    }

    //对象结构
    class ObjectStructor
    {
    public:
        //增加药品到药品列表
        void addMedicine(Medicine* p_mdc)
        {
            m_mdclist.push_back(p_mdc);
        }
        void procAction(Visitor* pvisitor)
        {
            for (auto iter = m_mdclist.begin(); iter != m_mdclist.end(); ++iter)
            {
                (*iter)->Accept(pvisitor);
            }
        }
    private:
        list<Medicine*> m_mdclist; //药品列表
    };

}

int main()
{

  _nmsp1::Visitor_SFRY visitor_sf; //收费人员访问者子类,里面承载着向我(患者)收费的算法
    _nmsp1::M_asplcrp mdc_asplcrp;
    _nmsp1::M_fftdnhsp mdc_fftdnhsp;
    _nmsp1::M_dlx m_dc_dlx;

    //各个元素子类调用Accept接受访问者的访问,就可以实现访问者要实现的功能
    mdc_asplcrp.Accept(&visitor_sf); //累加 阿司匹林肠溶片....价格
    mdc_fftdnhsp.Accept(&visitor_sf); //累加 氟伐他汀钠缓释片....价格
    m_dc_dlx.Accept(&visitor_sf); //累加 黛力新....价格    
    cout << "所有药品的总价为:" << visitor_sf.getTotalCost() << ",收费人员收取了我的费用!" << endl;

    _nmsp1::Visitor_QYRY visitor_qy; //取药人员访问者子类,里面承载着向我发放药品的算法
    mdc_asplcrp.Accept(&visitor_qy);  //我取得阿司匹林肠溶片
    mdc_fftdnhsp.Accept(&visitor_qy); //我取得氟伐他汀钠缓释片.
    m_dc_dlx.Accept(&visitor_qy); //我取得黛力新


    _nmsp1::Visitor_YYS visitor_yys; //营养师访问者子类,里面承载着为我配置营养餐的算法
    mdc_asplcrp.Accept(&visitor_yys);  //营养师针对治疗预防血栓的药 阿司匹林肠溶片 给出的对应的营养餐建议
    mdc_fftdnhsp.Accept(&visitor_yys); //营养师针对降低血脂的药 氟伐他汀钠缓释片 给出的对应的营养餐建议
    m_dc_dlx.Accept(&visitor_yys); //营养师针对治疗神经紊乱的药 黛力新 给出的对应的营养餐建议


    _nmsp1::ObjectStructor objstruc;
    objstruc.addMedicine(&mdc_asplcrp);
    objstruc.addMedicine(&mdc_fftdnhsp);
    objstruc.addMedicine(&m_dc_dlx);
    objstruc.procAction(&visitor_yys); //将一个访问者对象visitor_yys应用到一批元素上,以实现对一批元素进行同一种操作(营养方面的);


    return 0;


}

 

    //角色
    //a)Visitor(抽象访问者)
    //b)ConcreteVisitor(具体访问者)
    //c)Element(抽象元素)。指Medicine类。
    //d)ConcreteElement(具体元素):
    //e)ObjectStructor(对象结构):
 
 
第二种:

    //(3)访问者模式深入理解
    //双分派/二次分派/双重分派(Double-dispatch)调用机制。
    //双分派表示所要执行的操作取决于被访问者(元素)的类型和访问者的种类。

    //访问者模式优缺点:
    //a)增加新访问者容易,增加被访问者(元素)困难
    //b)与操作有关的代码都放到了访问者子类中
    //c)要求具体元素类的接口比较丰富。但是这也会导致元素类暴露太多公共操作接口而破坏封装性。

    //比较适合的场景
    //a)需要对元素对象结构进行很多不同的操作。 将对象自身与针对对象的操作分离的目的。
    //b)元素对象结构所属的类很少发生变动,但在元素对象结构上经常需要增加新操作。
    //c)一般访问者模式用的不太多,但一旦在需要的场合该模式也会显得无可替代。

 

再举个例子

在C++中实现访问者模式需要一些基本的设计和代码结构。访问者模式主要涉及到元素(Element)、具体元素(ConcreteElement)、访问者(Visitor)、具体访问者(ConcreteVisitor)和对象结构(ObjectStructure)等组件。

示例实现

假设我们有一个图书馆管理系统,其中有不同类型的图书(如小说、期刊等),我们希望能够为这些图书实现不同的操作(如借阅统计、归还处理等),而不改变图书类本身的结构。

1. 元素接口定义

// 元素接口
class Element {
public:
    virtual ~Element() {}
    virtual void accept(class Visitor &visitor) = 0;
};

2. 具体元素类定义

// 具体元素类:小说
class Novel : public Element {
public:
    void accept(Visitor &visitor) override {
        visitor.visitNovel(this);
    }
    // 其他小说特有的方法和属性
};

// 具体元素类:期刊
class Journal : public Element {
public:
    void accept(Visitor &visitor) override {
        visitor.visitJournal(this);
    }
    // 其他期刊特有的方法和属性
};

 

3. 访问者接口定义

// 访问者接口
class Visitor {
public:
    virtual ~Visitor() {}
    virtual void visitNovel(Novel *novel) = 0;
    virtual void visitJournal(Journal *journal) = 0;
};

4. 具体访问者类定义

// 具体访问者类:借阅统计访问者
class BorrowingStatisticsVisitor : public Visitor {
public:
    void visitNovel(Novel *novel) override {
        // 统计借阅小说的操作
        cout << "统计小说:" << novel->getTitle() << endl;
    }

    void visitJournal(Journal *journal) override {
        // 统计借阅期刊的操作
        cout << "统计期刊:" << journal->getTitle() << endl;
    }
};

// 具体访问者类:归还处理访问者
class ReturnProcessingVisitor : public Visitor {
public:
    void visitNovel(Novel *novel) override {
        // 处理小说归还的操作
        cout << "归还小说:" << novel->getTitle() << endl;
    }

    void visitJournal(Journal *journal) override {
        // 处理期刊归还的操作
        cout << "归还期刊:" << journal->getTitle() << endl;
    }
};

5. 对象结构类定义

// 对象结构类
class Library {
public:
    void attach(Element *element) {
        elements.push_back(element);
    }

    void detach(Element *element) {
        elements.remove(element);
    }

    void accept(Visitor &visitor) {
        for (auto elem : elements) {
            elem->accept(visitor);
        }
    }

private:
    list<Element *> elements;
};

 

6. 示例使用

int main() {
    Library library;

    // 添加一些图书
    Novel novel1;
    Journal journal1;
    library.attach(&novel1);
    library.attach(&journal1);

    // 创建具体访问者
    BorrowingStatisticsVisitor borrowingVisitor;
    ReturnProcessingVisitor returnVisitor;

    // 不同的访问者对图书进行不同操作
    library.accept(borrowingVisitor);
    library.accept(returnVisitor);

    return 0;
}

解释

  • 元素(Element):在示例中是 Element 抽象类,定义了 accept 方法,由具体的图书类(如 NovelJournal)继承并实现 accept 方法。

  • 访问者(Visitor):定义了对不同类型图书的访问方法,如 visitNovelvisitJournal

  • 具体访问者(ConcreteVisitor):实现了 Visitor 接口,定义了具体的访问操作,如 BorrowingStatisticsVisitorReturnProcessingVisitor

  • 对象结构(ObjectStructure):在示例中是 Library 类,管理了图书的集合,可以接受不同访问者的访问。

注意事项

  • 访问者模式适用于当一个对象结构中的元素类很少变化,但经常需要在此对象结构上定义新的操作时。
  • 在示例中,为了简化代码,省略了图书类的详细实现,实际应用中可能还需要考虑图书的具体属性和方法。

这个示例展示了如何在C++中实现访问者模式,通过访问者模式可以方便地增加新的操作,同时避免修改现有的元素类

 

posted @ 2024-07-12 11:09  白伟碧一些小心得  阅读(22)  评论(0编辑  收藏  举报