消除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模式。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值