编程模式之访问者

访问者模式,个人理解为是一个分类器,可以把抽象类型恢复到具体类型。面向对象的子类父类就是为了解决抽象、具体的问题。抽象的接口不好定义,免不了少定义了,又要改接口,而有些接口可能还不是很适用于每个具现类,这个时候是要定义抽象接口还是不需要呢,程序员又得费脑细胞了。访问者模式可以方便的将抽象类型落实到具体类型对象。也就是说,你给我一个箱子说装着一个动物,让我给准备食物,通过访问者模式就知道了它原来是一个狗狗,那就准备骨头吧。而且随时可以组织一个喂食访问者、或者一个防疫访问者,不用去修改动物这个类型的接口。这个模式也是实际使用最多的模式之一。

一般来说访问者模式要有一个Element抽象类,定义被访问者的基础方法Accept。一个访问者基类Visitor,定义了所有Element的具现类的访问接口。所以一般来说要求Eelement的具现类要稳定,否则,Visitor接口要反复修改就不太妙了。而且如果有些具体Visitor只处理一部分Element种类的情况也麻烦,还说上面的动物,如果一个具体的Visitor要看一个动物是否是鸡,如果是鸡就打预防针,否则不管。就这样的一个功能,还要处理其他各种动物的接口就不美了。

下面介绍本人实现的访问者模式,可以灵活组织,避免不少麻烦,经过多年的应用,基本没有修改过。

//所有Visitor的基类

//让所有的Visitor有一个统一的接口

class Base_Visitor

{

public:

    virtual ~Base_Visitor(){};

};

//对每个具体的Visitee进行访问的基类

template<class T,typename R>

class Visitor

{

public:

    virtual ~Visitor(){};

    typedef  R ReturnType;

public:

    //对Visitee对象的访问和扩展

    virtual  ReturnType  Visit(T&) = 0;

};

template<typename R = void>

class  Visitee

{

public:

    virtual ~Visitee(){};

    typedef  R  ReturnType;

public:

    //和宏DEFINE_Visitee配合把具体Visitee和visitor建立关联

    virtual  ReturnType  Accept(Base_Visitor& visitor) = 0;

protected:

    //静态函数,目的是为了visitor能访问具体的Visitee对象

    //也是和DEFINE_Visitee配合,对具体的Visitee对象T进行访问注册

    template<class T>

    static  ReturnType  AcceptImpl(T& visitee, Base_Visitor& guest)

    {

        Visitor<T, ReturnType>* p = dynamic_cast<Visitor<T, ReturnType>*>(&guest);

        //把具体的Visitee注册到对应的Visitor中

        //Visitor由BaseVisitor派生,并且以具体的Visitee类为模板参数

        if(NULL != p)

        {

            return p->Visit(visitee);

        }

        return ReturnType();

    }

};

#define DEFINE_VISITEE() \

    public:                \

    virtual ReturnType Accept(Base_Visitor& visitor) \

{ return AcceptImpl(*this, visitor); }  

应用示例:

class A : public Visitee<bool>

{

     DEFINE_VISITEE()

}

class B : public Visitee<bool>

{

    DEFINE_VISITEE()

}

Class C : public Visitee<bool>

{

    DEFINE_VISITEE()

}

class ABVisitor : public Base_Visitor,public Visitor<A,bool>,public Visitor<B,bool>

{

       virtual bool Visit(A& obj)

{

     //do somthing

     return true;

}

virtual bool Visit(B& obj)

{

   return true;

}

}

C cObj;

ABVisitor v;

bool b = cObj.Accept(v);//b == false;

B bObj;

b = bObj.Accept(v);//b == true;

如例:Visitor可以随时组织,可以对被访问者的一部分进行访问操作,也可以对全体进行访问操作。新写的代码不影响以前的代码,即使多了对象D,如果非逻辑需求,以前写的Visitor也可以不处理。

比如做一个图形系统:

圆、矩形、多边形、扇形、直线、折线都继承自Visitee。将一张图中的所有对象序列化,就可以用一个Visitor,将一张图所有对象写成XML格式文件可以用另一个Visitor,可以有一个Visitor给所有有面积的设置填充颜色。当鼠标选中一个图形对象的时候,可以用一个Visitor给这个选中对象分配对应的编辑对象来处理鼠标键盘事件。总之,需要针对不同对象处理什么事情的时候就可以写一个Visitor,很方便扩展。不用修改以前的代码,非常符合模式的基本原则。

访问者模式可以和消息树结合应用,减少消息树不必要的动态类型转换,也就是利用访问者模式,将消息具体到具体消息,再进行具体操作,读者可以先自行考虑,应该是水到渠成。笔者有时间再写吧。

上一篇:js中的switch的范围匹配用法


下一篇:Java基础面试题-5