设计模式-----Visitor模式

     关于设计模式讲解的资料多如牛毛,不知道是我的天生愚钝误入了这个高端的行业,还是资料讲解的不清楚,对于Visitor设计模式我看了很多遍,始终不能理解其中的奥妙。

今天突然看到了几个字让我有些豁然开朗,Visitor模式在软件设计之初大部分不会用到也不会想到。但是在软件成品以后对于扩展原来的功能很有帮助,而且每个Visitor的实例是对整个被操作者子类的扩展功能的封装,而不是每个Visitor子类对应一个被操作者子类。而且是在被操作者不能再增加子类的情况下的操作。限制条件很多,等到用到时就是产品已经很成熟的时候。

    以下是引用:Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1009585

    考虑一下设计一个可以包含长方形、正方形、圆形以及文字的视图类。因为视图中每种元素的显示方式都不一样,所以我们很容易做出如下的设计:

    在这里,我使用了COMPOSITE模式。对于COMPOSITE模式,可以参考我那篇《重读《设计模式》之学习笔记(五)--我对COMPOSITE模式的理解》
    现在,我们来分析一下上面的这个设计方案。如果说,由于客户需求的改变,视图中每个元素的显示方法变了,那么我就就要更改每个类的Show方法(当然,客户还有把需求改回去的可能,那样我们还得把Show方法给改回去。虽然这样会使程序员痛苦不堪,但是这种可能还是存在的。我想,做开发的人都会遇到类似的情况。本书中第7章就有这样两句话:“软件开发最重要的事实之一:需求总是在变化。”,“在大多数软件项目中最不稳定的东西就是需求。”)。另外一种情况,客户要求视图可以通过打印机打印,那我们就得给每一个类就要加上一个Print方法。或者,客户要求可以统计视图中每种元素的个数,那么我们还得给类CTView添加一个统计的方法。每次更改,所有使用这些类的代码都要重新编译。
    从上面的分析可以看出,我们这个设计是不成功的。在这个时候我们就应该使用VISITOR模式。VISITOR模式的好处就是可以在不改变现有类层次结构的情况下向其中增加新方法。听起来似乎很神奇,下面我就用代码来说明:
    首先,创建一个CVisitor的抽象类:

class  CVisitor
{
public :
    
virtual   ~ CVisitor();
    
virtual   void  VisitRectangle(CTRectangle  * pRectangle)  =   0 ;
    
virtual   void  VisitSquare(CTSquare  * pSquare)  =   0 ;
    
virtual   void  VisitCircle(CTCircle  * pCircle)  =   0 ;
    
virtual   void  VisitText(CTText  * pText)  =   0 ;
    
virtual   void  VisitView(CTView  * pView)  =   0 ;
};

    我们要通过该类的子类来访问视图中的各个元素。
    下面是重新设计后视图及其各个元素的代码:

class  CContext
{
public :
    
virtual   ~ CContext();
    
virtual   void  Accept(CVisitor &  v)  =   0 ;
};


class  CTRectangle :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) { v.VisitRectangle( this ); };
};


class  CTSquare :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) { v.VisitSquare( this ); };
};


class  CTCircle :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) { v.VisitCircle( this ); };
};

class  CTText :  public  CContext
{
public :
    
void  Accept(CVisitor &  v) { v.VisitText( this ); };
};


class  CTView :  public  CContext
{
public :
    
~ CTView();
    
void  Accept(CVisitor &  v);
    
void  Add(CContext  * pContext);

private :
    vector
< CContext *>  m_vContext;
};

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

        delete pContext;
    }
}

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

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

    下面,我们为上面的类添加一个显示视图中各个元素并且计算各个元素个数的visitor:

class  CShowContextVisitor :  public  CVisitor
{
public :
    CShowContextVisitor();

    
void  VisitRectangle(CTRectangle  * pRectangle);
    
void  VisitSquare(CTSquare  * pSquare);
    
void  VisitCircle(CTCircle  * pCircle);
    
void  VisitText(CTText  * pText);
    
void  VisitView(CTView  * pView);

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

CShowContextVisitor::CShowContextVisitor()
: m_iRectangleCount(
0 )
, m_iSquareCount(
0 )
, m_iCircleCount(
0 )
, m_iTextCount(
0 )
{}

//  下面以输出一句话来代替具体的显示 

void  CShowContextVisitor::VisitRectangle(CTRectangle  * pRectangle)
{
    cout 
<<   " A Rectangle is Showed! "   <<  endl;

    m_iRectangleCount
++ ;    
}

void  CShowContextVisitor::VisitSquare(CTSquare  * pSquare)
{
    cout 
<<   " A Square is Showed! "   <<  endl;

    m_iSquareCount
++ ;  
}

void  CShowContextVisitor::VisitCircle(CTCircle  * pCircle)
{
    cout 
<<   " A Circle is Showed! "   <<  endl;

    m_iCircleCount
++ ;  
}

void  CShowContextVisitor::VisitText(CTText  * pText)
{
    cout 
<<   " A Text is Showed! "   <<  endl;

    m_iTextCount
++ ;  
}

void  CShowContextVisitor::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;
}

    我们可以用下面的测试函数来验证我们的设计是否正确:

void  Test()
{
    CTView TestView;

    CTRectangle 
* pRectangle  =   new  CTRectangle;
    TestView.Add(pRectangle);

    CTSquare 
* pSquare  =   new  CTSquare;
    TestView.Add(pSquare);

    CTCircle 
* pCircle  =   new  CTCircle;
    TestView.Add(pCircle);

    CTText 
* pText  =   new  CTText;
    TestView.Add(pText);    

    CShowContextVisitor TestVisitor;
    TestView.Accept(TestVisitor);
}

    当然,输出跟我们期望的一样:

   A Rectangle is Showed!
   A Square is Showed!
   A Circle is Showed!
   A Text is Showed!
   A View is Showed!
  Rectangle count: 1
  Square count: 1
  Circle count: 1
  Text count: 1

    如果客户提出其他需求,我们只要添加一个相应的visitor就行了,而不用修改我们设计好的类。就算客户把需求改回去,只要重新使用我们以前写好的visitor的文件就行了。
    VISISTOR模式最大的好处就是:很容易增加新的操作,而且能把这些相关的操作应该集中在一起,也就是visitor类里。
    但是,VISITOR模式有一个致命的弱点,那就是添加相关的元素类比较困难。比如上面的例子中,如果客户要求可以在视图中添加三角形,那么我们除了要写一个CTTriangle类以外,还得修改抽象类CVisitor和它所有的子类。
    所以,我们应该在类的层次结构已经固定而操作需要较多的添加或修改时才选择使用VISITOR模式。 



 


 

猜你喜欢

转载自blog.csdn.net/tianyapai/article/details/8638608
今日推荐