消除VISITOR模式中的循环依赖

2024-02-01 12:48

本文主要是介绍消除VISITOR模式中的循环依赖,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

    在我的那篇《VISITOR模式--《敏捷软件开发》读书笔记(三)》中,我用一个C++的小例子说明了设计模式中的VISITOR模式。在例子代码中,我们可以发现:为了使VISITOR类CVisitor通过编译,它就必须知道它要访问的类(CTRectangle,CTSquare,CTCircle, CTText ,CTView )的定义;而这些被访问的类要通过编译,它们必须知道类CVisitor的定义。这样就形成了循环依赖。如下面的类图(带箭头的虚线表示依赖关系): 

    可以看到,由于循环依赖,visitor类和被访问的类之间的依赖关系都是双向的,这张类图看上去跟蜘蛛网差不多。
    虽然我们可以用前置声明来解决编译问题,但是这样的设计会给代码维护带来非常大的麻烦!下面,还用原来的例子,来设计一个消除掉循环依赖的VISITOR模式。
    首先,定义一个VISTOR的基类:

class  CVisitor
{
public :
    
virtual   ~ CVisitor() {}
};

    实际上,这个VISITOR的基类什么都不做,它只是具体类型信息的载体。虽然这样,类CVisitor却非常重要,因为它为VISITOR类提供了RTTI(Run-Time Type Identification)能力。我们可以用dynamic_cast来把CVisitor的指针转换为我们想要的具体的VISITOR类对象的指针。
    然后,针对VISITOR类要访问的每一个类,定义一个小型的VISITOR类:

class  CRectangleVisitor
{
public :
    
virtual   void  VisitRectangle(CTRectangle * =   0 ;
};

class  CSquareVisitor
{
public :
    
virtual   void  VisitSquare(CTSquare * =   0 ;
};

class  CCircleVisitor
{
public :
    
virtual   void  VisitCircle(CTCircle * =   0 ;
};

class  CTextVisitor
{
public :
    
virtual   void  VisitText(CTText * =   0 ;
};

class  CViewVisitor
{
public :
    
virtual   void  VisitView(CTView * =   0 ;
};

    这些小型的抽象VISITOR类只定义了访问的接口函数,由具体的VISITOR类来实现这些函数。
    现在,来修改被访问的类:

class  CContext
{
public :
    
virtual   ~ CContext() {}

    
virtual   void  Accept(CVisitor &  v)  =   0 ;
};

class  CTRectangle :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) 
    {
        
if (CRectangleVisitor  * pVisitor  =  dynamic_cast < CRectangleVisitor *> ( & v))
            pVisitor
-> VisitRectangle( this ); 
    }
};

class  CTSquare :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) 
    {
        
if (CSquareVisitor  * pVisitor  =  dynamic_cast < CSquareVisitor *> ( & v))
            pVisitor
-> VisitSquare( this ); 
    }
};

class  CTCircle :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) 
    {
        
if (CCircleVisitor  * pVisitor  =  dynamic_cast < CCircleVisitor *> ( & v))
            pVisitor
-> VisitCircle( this ); 
    }
};

class  CTText :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) 
    {
        
if (CTextVisitor  * pVisitor  =  dynamic_cast < CTextVisitor *> ( & v))
            pVisitor
-> VisitText( this ); 
    }
};

class  CTView :  public  CContext
{
public :
    
~ CTView()
    {
        
while ( ! m_vContext.empty())
        {
            CContext 
* pContext  =  (CContext * )m_vContext.back();
            m_vContext.pop_back();

            delete pContext;
        }
    }

    
void  Accept(CVisitor &  v)
    {
        
for (vector < CContext *> ::iterator i  =  m_vContext.begin(); i  !=  m_vContext.end();  ++ i)
        {
            (
* i) -> Accept(v);
        }
        
        
if (CViewVisitor  * pVisitor  =  dynamic_cast < CViewVisitor *> ( & v))
            pVisitor
-> VisitView( this );
    }

    
void  Add(CContext  * pContext)
    {
        m_vContext.push_back(pContext);
    }

private :
    vector
< CContext *>  m_vContext;
};

    上面的代码跟原来的不同之处就是:每个Accept方法里面多了一个if语句。在这个if语句中,通过dynamic_cast将传入的参数visitor转换成我们需要的visitor,然后再调用具体的访问函数。
    下面,跟《VISITOR模式--《敏捷软件开发》读书笔记(三)》一样,我们为上面的类添加一个显示视图中各个元素并且计算各个元素个数的visitor:

class  CShowContextVisitor :
    
public  CVisitor,
    
public  CRectangleVisitor,
    
public  CSquareVisitor,
    
public  CCircleVisitor,
    
public  CTextVisitor,
    
public  CViewVisitor
{
public :
    CShowContextVisitor()
        : m_iRectangleCount(0),
          m_iSquareCount(0),
          m_iCircleCount(0),
          m_iTextCount(0)
    {}


    
void  VisitRectangle(CTRectangle  * pRectangle)
    { 
        cout 
<<   " A Rectangle is Showed! "   <<  endl; 
        m_iRectangleCount
++ ;
    }

    
void  VisitSquare(CTSquare  * pSquare)
    { 
        cout 
<<   " A Square is Showed! "   <<  endl;
        m_iSquareCount
++ ;
    }

    
void  VisitCircle(CTCircle  * pircle)
    {
        cout 
<<   " A Circle is Showed! "   <<  endl;
        m_iCircleCount
++ ;
    }

    
void  VisitText(CTText  * pText)
    {
        cout 
<<   " A Text is Showed! "   <<  endl;
        m_iTextCount
++ ;
    }

    
void  VisitView(CTView  * pView)
    {
        cout 
<<   " A View is Showed! "   <<  endl;
        cout 
<<   " Rectangle count:  "   <<  m_iRectangleCount  <<  endl;
        cout 
<<   " Square count:  "   <<  m_iSquareCount  <<  endl;
        cout 
<<   " Circle count:  "   <<  m_iCircleCount  <<  endl;
        cout 
<<   " Text count:  "   <<  m_iTextCount  <<  endl;
    }

private :
    
int  m_iRectangleCount;
    
int  m_iSquareCount;
    
int  m_iCircleCount;
    
int  m_iTextCount;
};

    从上面的代码可以看出,这个类CShowContextVisitor跟原来那篇文章中的实现没有区别,唯一的区别就是:它是从VISITOR的基类CVisitor和那些小型的抽象VISITOR类继承的。这样就可以保证在Accept函数中用dynamic_cast可以动态转换为我们需要的具体VISITOR类,从而调用相应的访问函数。
    下面是这个新设计方案的类图:

    从图中可以看出,原来的循环依赖已经被消除!
    我们可以用《VISITOR模式--《敏捷软件开发》读书笔记(三)》中一样的测试函数来对这个新设计的方案进行测试,当然结果也跟那篇文章一样,都是正确的。
    古人云:有得必有失。这里要说明的是,新的设计方案虽然消除了循环依赖,但是却引入了dynamic_cast。而dynamic_cast在运行期是需要一些时间成本来进行动态类型转换的。如果你的程序对效率要求比较高,那你就不得不用原来的带有循环依赖性的VISITOR模式。

这篇关于消除VISITOR模式中的循环依赖的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/667337

相关文章

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

Spring AI Alibaba接入大模型时的依赖问题小结

《SpringAIAlibaba接入大模型时的依赖问题小结》文章介绍了如何在pom.xml文件中配置SpringAIAlibaba依赖,并提供了一个示例pom.xml文件,同时,建议将Maven仓... 目录(一)pom.XML文件:(二)application.yml配置文件(一)pom.xml文件:首

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

使用maven依赖详解

《使用maven依赖详解》本文主要介绍了Maven的基础知识,包括Maven的简介、仓库类型、常用命令、场景举例、指令总结、依赖范围、settings.xml说明等,同时,还详细讲解了Maven依赖的... 目录1. maven基础1.1 简介1.2 仓库类型1.3 常用命令1.4 场景举例1.5 指令总结

Python判断for循环最后一次的6种方法

《Python判断for循环最后一次的6种方法》在Python中,通常我们不会直接判断for循环是否正在执行最后一次迭代,因为Python的for循环是基于可迭代对象的,它不知道也不关心迭代的内部状态... 目录1.使用enuhttp://www.chinasem.cnmerate()和len()来判断for

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

JAVA中while循环的使用与注意事项

《JAVA中while循环的使用与注意事项》:本文主要介绍while循环在编程中的应用,包括其基本结构、语句示例、适用场景以及注意事项,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录while循环1. 什么是while循环2. while循环的语句3.while循环的适用场景以及优势4. 注意

python中poetry安装依赖

《python中poetry安装依赖》本文主要介绍了Poetry工具及其在Python项目中的安装和使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前言1. 为什么pip install poetry 会造成依赖冲突1.1 全局环境依赖混淆:1

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论