visitor模式

来源:互联网 发布:东坡软件官方下载 编辑:程序博客网 时间:2024/06/10 04:51

Visitor定义
  作用于某个对象群中各个对象的操作。它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作。

http://perfectivan.spaces.live.com/blog/cns!6E75BD43D902FA37!125.entry

使用visitor模式重写scott meyers的effective c++中的条款

 

scott meyers 的大作More Effective c++相信很多c++程序员已经是熟读,我也不例外,其中有条款31是关于 “double-dispatch”.最近系统学习了设计模式,其中的visitor模式让人印象深刻,同样是使用“double-dispatch”模式. Erich gammavisitor模式中的设计就比scott meyers中的更加巧妙,我说这话绝对没有侮辱大师的意思,只是觉得,术业有专攻,也许当时scott meyers并没有看到设计模式这本书,也或者是scott meyers在实现“double-dispatch”的时候更加专注于c++的实现本身.

我写这篇文章希望将两个人对“double-dispatch”的实现分别进行描述,从而找出为什么面向对象的编程比面向过程的编程再软件复用上更加有优势,同时使用victor模式将scott meyers中对“double-dispatch”的实现方法进行改写.

  effective c++的条款31 ,scott meyers描述了这样的一个需求:

事情的发生地点是太空,他要求模拟,宇宙飞船,太空站,以及小行星.相互碰撞的过程.

  scott meyers一开始将我的引入面向对象的设计思维中,.从下面的类图我们很清楚的看到宇宙飞船,太空站,以及小行星都是从Gameobject中派生而来,没有问题,毕竟大家都有相同的接口,比如:Fly(飞行),Port(停靠)等等.框架既然已经搭建好了,就让我们专注于事物的逻辑吧,在本需求中业务的逻辑就是,如何处理任意两个Gameobject的碰撞问题.可以将处理写成一个函数like this:

        Collide(Gameobject&);

Collide将会成为一个virtual member function.

这样简单的处理,将会面临的问题,就是传说中的“double-dispatch”.也就是virtual member function的行为不再只单一依赖于函数的调用者.同时也依赖于函数的参数.(对比之下很容易就想到什么是”single-dispatch”了吧).在这个caseCollide的方法的行为不再仅仅取决于左边的this,同样取决两个形参.

看看最初scott meyers给出的solution,

void classname::Collide(Gameobject& otherObject)
{ 
      if(typeid(otherObject) == spaceShip) 
      { 
            //some stuff 
      } 
      else if(typeid(otherObject) == spacestation) 
      { 
           //some stuff 
      } 
      else 
      { 
          //some stuff 
      }
}

bulabula,如此丑陋的solution让我想起了当年使用c语言进行面向过程编程的蛮荒以及无知日子.这一点scott meyers当然知道,所以他又给出了一个更为精巧的解决的方案,以致于让我觉得scott meyers在故意卖弄他那丰富的c++技术知识.  

Class spaceShip:public GameObject
{
      // 为什么是static,不同的spaceShip共享???对有可能,因为这个map可能是动态改变的
      static Hitmap cllisionMap; 
      typedef void (spaceShip::*HitFunctionPtr)( Gameobject&); 
      static HitFunctionPtr lookupfunptr();

       virtual void Collide(Gameobject&);

      virtual void hitSpaceShip(Gameobject& spaceShip); 
 
     virtual void hitSpaceStation(Gameobject & spaceStation); 
 
     virtual void hitAsteroid(Gameobject &spaceasteroid);

};

后面3个函数如此的不同以致于让我用红颜色对他们进行标注.使用这3个函数,“double-dispatch”简化,使其更加优雅.在此当中Hitmap是一个map存放的是member function的指针,也就是使用红颜色标注的函数的指针. scott meyers希望当程序初始化时候将这些函数指针存入map,他们在map中的key是函数要处理的相应的对象名字,hitSpaceShip中的keySpaceShip.依次类推.当对象对Collide函数进行调用从而处理撞击事件的时候再其内部查询形参的类型(使用typeid)从而从map中取出相应的函数指针,进行调用.

大概的实现就像这样:

void SpaceShip:: Collide(Gameobject& otherobject)
{
        // :这里通过查找函数避免了一个一个的比较
      HitFunctionPtr funcPtr = lookup(otherobject) 
      { 
            if(funcPtr) 
                  this->*funcPtr(otherobject); 
      }     
}

这样的solution真是漂亮,使用了c++编译器很多深层原理.以后scott meyers又一次完善了他的设计,也逃不过,过程化的厄运.也就是,如果我想向工程中加入一个新的空间物体,那我还需要重写每个已有类的Collide代码.scott meyers都承认,如果想派生新的类只需修改几行代码就可以.不理解scott meyers当时写这句话的感受,是无奈还是洋洋得意?连大师都没辙了.更何况我们这些小小的程序员.

在了解的visitor模式后转机产生了.

原创粉丝点击