首页 > 代码库 > 访问者模式

访问者模式

1、前奏理解

  书中是以人类的性别来引出这个模式,因为只有两种性别,也就是说数据结构是稳定的,因此非常适合用来理解访问者模式,而这也是访问者模式使用的前提“数据结构相对稳定”。但是,有一句话说的我也是一惊:这算是GoF中最复杂的一个模式!所以这里我仅仅把我未入门的理解写下来。

2、定义

  访问者模式(Visitor)表示一个作用于某对象结构中的各元素的操作;它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

3、使用与分析

  用处:访问者模式适用于数据结构相对稳定的系统;它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以比较自由的演化。从他的用处上可以看出来,他的目的就是把数据处理从数据结构分离出来。

  优点:访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者;而访问者模式将有关的行为集中到一个访问者对象中。

  缺点:从他的适用情形就可以看出来,他的缺点就是增加新的数据结构变得困难了。

 4、代码示例

下面以书中的例子来简单理解一下

1)首先需要建立两个抽象类:元素的基类,状态的基类:

技术分享
abstract class Person
    {
        /// <summary>
        /// 获取“状态”对象
        /// </summary>
        /// <param name="pVisitor"></param>
        public abstract void Accept(vAction pVisitor);
    }

    abstract class vAction
    {
        //得到元素A的反应或结论
        public abstract void GetManConclusion(Man pConcreteElementA);

        //得到元素B的结论或反应
        public abstract void GetWomanConclusion(Woman pConcreteElementB);
    }
View Code

2)建立具体的状态类

    /*
     具体的状态类,每一种操作建立为一个状态类
     */
    class Success:vAction
    {
        public override void GetManConclusion(Man pConcreteElementA)
        {
            Console.WriteLine("{0}{1}时,背后多半有一个伟大的女人!",
                pConcreteElementA.GetType().Name,this.GetType().Name);
        }

        public override void GetWomanConclusion(Woman pConcreteElementB)
        {
            Console.WriteLine("{0}{1}时,背后多半有一个不成功的男人!",
                pConcreteElementB.GetType().Name, this.GetType().Name);
        }
    }
    class Fail : vAction
    {
        public override void GetManConclusion(Man pConcreteElementA)
        {
            Console.WriteLine("{0}{1}时,闷头喝酒,谁也不用劝!",
                pConcreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void GetWomanConclusion(Woman pConcreteElementB)
        {
            Console.WriteLine("{0}{1}时,眼泪汪汪,谁也劝不了!",
                pConcreteElementB.GetType().Name, this.GetType().Name);
        }
    }

    class Amativeness : vAction
    {
        public override void GetManConclusion(Man pConcreteElementA)
        {
            Console.WriteLine("{0}{1}时,凡事不懂也要装懂!",
                pConcreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void GetWomanConclusion(Woman pConcreteElementB)
        {
            Console.WriteLine("{0}{1}时,遇事懂也要装作不懂!",
                pConcreteElementB.GetType().Name, this.GetType().Name);
        }
    }


    //如果需要新增一个状态/算法,那么只需要在这里增加一个状态类即可
    class Marriage : vAction
    {
        public override void GetManConclusion(Man pConcreteElementA)
        {
            Console.WriteLine("{0}{1}时,感慨道:恋爱游戏结束时,有期徒刑遥无期!",
                pConcreteElementA.GetType().Name, this.GetType().Name);
        }

        public override void GetWomanConclusion(Woman pConcreteElementB)
        {
            Console.WriteLine("{0}{1}时,感慨道:爱情长跑路漫漫,婚姻保险报平安!",
                pConcreteElementB.GetType().Name, this.GetType().Name);
        }
    }

3)创建具体的元素类,相当于是具体的数据结构操作类:

/*
     双分派技术:
     * 首先在客户端中将具体状态作为参数传递给“男人”类,完成了一次分派;
     * 然后“男人”类调用作为具体状态中的方法“男人反应”同时将自己作为参数传递进去,这就完成了第二次分派
     */
    class Man:Person
    {
        public override void Accept(vAction pVisitor)
        {
            pVisitor.GetManConclusion(this);
        }
    }

    class Woman : Person
    {
        public override void Accept(vAction pVisitor)
        {
            pVisitor.GetWomanConclusion(this);
        }
    }

4)还需要创建一个对象结构类,由于总是需要不同元素之间不同状态的操作,所以还需要创建一个对象结构,遍历所有元素,然后得到每一个元素的操作:

class ObjStructure
    {
        private IList<Person> mc_Elements = new List<Person>();

        /// <summary>
        /// 增加一个要素
        /// </summary>
        /// <param name="pElement"></param>
        public void AddElement(Person pElement)
        {
            mc_Elements.Add(pElement);
        }

        /// <summary>
        /// 移除一个要素
        /// </summary>
        /// <param name="pElement"></param>
        public void DetachElement(Person pElement)
        {
            mc_Elements.Remove(pElement);
        }

        public void Display(vAction pVisitor)
        {
            foreach (Person p in mc_Elements)
                p.Accept(pVisitor);
        }
    }

5)在客户端中通过对象结构传递参数状态来得到每一个要素的具体反应:

static void Main(string[] args)
        {
            //在对象结构中加入要对比的要素
            //其实达到的效果就是一系列元素的反应
            ObjStructure o = new ObjStructure();
            o.AddElement(new Man());
            o.AddElement(new Woman());

            //不同状态时不同要素的反应
            Success lcSuccess = new Success();
            o.Display(lcSuccess);
            Fail lcFail = new Fail();
            o.Display(lcFail);
            Amativeness lcAmative = new Amativeness();
            o.Display(lcAmative);
            //如果要增加算法,那么只需要增加一个状态类即可,别的都不需要动,就可以将这个新的状态操作
            //用于所有已经有的类上面
            Marriage lcMarriage = new Marriage();
            o.Display(lcMarriage);

            Console.ReadKey();
        }

  以上是根据书中的例子具体抄写的代码,感觉比纯理论代码更容易理解。

访问者模式