消除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

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Java中的for循环高级用法

《Java中的for循环高级用法》本文系统解析Java中传统、增强型for循环、StreamAPI及并行流的实现原理与性能差异,并通过大量代码示例展示实际开发中的最佳实践,感兴趣的朋友一起看看吧... 目录前言一、基础篇:传统for循环1.1 标准语法结构1.2 典型应用场景二、进阶篇:增强型for循环2.

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如

Python循环结构全面解析

《Python循环结构全面解析》循环中的代码会执行特定的次数,或者是执行到特定条件成立时结束循环,或者是针对某一集合中的所有项目都执行一次,这篇文章给大家介绍Python循环结构解析,感兴趣的朋友跟随... 目录for-in循环while循环循环控制语句break语句continue语句else子句嵌套的循

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

Java -jar命令如何运行外部依赖JAR包

《Java-jar命令如何运行外部依赖JAR包》在Java应用部署中,java-jar命令是启动可执行JAR包的标准方式,但当应用需要依赖外部JAR文件时,直接使用java-jar会面临类加载困... 目录引言:外部依赖JAR的必要性一、问题本质:类加载机制的限制1. Java -jar的默认行为2. 类加