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

相关文章

javacv依赖太大导致jar包也大的解决办法

《javacv依赖太大导致jar包也大的解决办法》随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,:本文主要介绍javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的... 目录前言1.检查依赖2.更改依赖3.检查副依赖总结 前言最近在写项目时,用到了Javacv里的获取视频

Spring 依赖注入与循环依赖总结

《Spring依赖注入与循环依赖总结》这篇文章给大家介绍Spring依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Spring 三级缓存解决循环依赖1. 创建UserService原始对象2. 将原始对象包装成工

Spring-DI依赖注入全过程

《Spring-DI依赖注入全过程》SpringDI是核心特性,通过容器管理依赖注入,降低耦合度,实现方式包括组件扫描、构造器/设值/字段注入、自动装配及作用域配置,支持灵活的依赖管理与生命周期控制,... 目录1. 什么是Spring DI?2.Spring如何做的DI3.总结1. 什么是Spring D

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

C#和Unity中的中介者模式使用方式

《C#和Unity中的中介者模式使用方式》中介者模式通过中介者封装对象交互,降低耦合度,集中控制逻辑,适用于复杂系统组件交互场景,C#中可用事件、委托或MediatR实现,提升可维护性与灵活性... 目录C#中的中介者模式详解一、中介者模式的基本概念1. 定义2. 组成要素3. 模式结构二、中介者模式的特点

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

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