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

在这里,我使用了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模式。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1009585

visitor 模式相关推荐

  1. 行为模式之Visitor模式

    1.意图 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作 2.适用性 (1)一个对象结构包含很多类对象,它们有不同的接口,而你想对这些实施一些 ...

  2. 设计模式--访问器(Visitor)模式

    模式定义 表示一个作用于某对象结构中的各元素的操作,使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化) 类图 要点总结 Visitor模式通过所谓双重分发(doubl ...

  3. 设计模式学习笔记——访问者(Visitor)模式

    设计模式学习笔记--访问者(Visitor)模式 @(设计模式)[设计模式, 访问者模式, visitor] 设计模式学习笔记访问者Visitor模式 基本介绍 访问者案例 类图 实现代码 Visit ...

  4. 设计模式 - Visitor 模式(访问者模式)

    作用:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.  UML结构图: 解析: Visitor模式把对结点的访问封装成一个抽象基类,通过派生 ...

  5. Visitor模式学习

    意图: 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 似乎理解很麻烦,其实说白了,就是动态的给已经写好的代码(类)加入新的功能! 当然,首 ...

  6. 设计模型之Visitor模式-图书馆管理系统应用C++实现

    今天看到<<软件设计师>>面向对象第七章讲到几种设计模式,其中一种为Visitor模式.这种设计模式通过访问者对象的建立,在访问过程中将访问者作为参数传到被访问对象的函数中,实 ...

  7. Visitor模式与Acyclic Visitor

    visitor模式最基本的是访问者和被访问者.它的优势是不用改变被访问类的任何结构就可以对被访问者施加新的操作,前提是增加一个访问者的子类.其实就是用增加访问者子类的方法替代被访问者方法的增加. 用你 ...

  8. visitor模式入门

    访问差异类型的集合类--visitor模式入门 一,问题提出 访问同一类型的集合类是我们最常见的事情了,我们工作中这样的代码太常见了. 1 Iterator ie  =  list.iterator( ...

  9. visitor模式本质

    http://topic.csdn.net/t/20051231/14/4492965.html visitor模式的本质是在不修改类结构的前提下为类及所有派生类添加一个"虚函数" ...

最新文章

  1. DllMain使用的注意事项
  2. 收集的一些操作系统面试题
  3. dapper 注意事项之GUID
  4. c8051单片机注意事项:
  5. 《卓有成效的程序员》读书笔记
  6. Machine Vision Pixel Calibration~ ~ ~ ~ ~ ~ ~ ~ ~ ~
  7. log4j日志文件 log4j.xml log4j.properties配置
  8. orangepi自启动打开一个终端并且运行脚本
  9. 洛谷P4074 [WC2013]糖果公园(莫队)
  10. 排查链接是否失效_【知了堂信安笔记】Linux入侵排查
  11. python编写自动更换ip工具的代码
  12. ESP32-8位数码管
  13. linux压缩超过4g的文件吗,使用tar压缩文件,不能超过4G,有什么办法解决-
  14. 支付宝接口http请求及sign加密
  15. android 车载安富蓝牙电话开发,Android平台BLE低功耗蓝牙开发
  16. 数字化转型道阻且长?免费低代码平台也能“神助攻”
  17. AJAX_入门经典案例
  18. MindSpore首发:基于贝叶斯的图神经网络推荐方法,提升新用户和交互稀疏用户推荐的点击率
  19. makefile中一些符号的含义
  20. MAT(Memory Analyzer Tool)工具使用超详细版

热门文章

  1. 计算机网络实验三——IP网络规划与路由设计
  2. vi颜色显示设置(centos7)
  3. 一文带你了解 Flink Forward 柏林站全部重点内容
  4. 俞敏洪大学经典励志演讲:像树一样活着
  5. PHP 实现递归处理数据
  6. Python基础学习视频
  7. 电脑耳机没声音怎么设置?(win7/win10电脑耳机没声音的解决方法)
  8. stm32 hal库分析之uart
  9. 关于HMM(隐马尔可夫模型)
  10. python基础第二课(小白piao分享)