【22】访问者模式(Visitor Pattern)

  一、引言
  在这篇博文中,我将为大家分享我对访问者模式的理解。

  二、访问者模式介绍

  2.1 访问者模式的定义

  访问者模式是封装一些施加于某种数据结构之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保存不变。访问者模式适用于数据结构相对稳定的系统, 它把数据结构和作用于数据结构之上的操作之间的耦合度降低,使得操作集合可以相对自由地改变。
  数据结构的每一个节点都可以接受一个访问者的调用,此节点向访问者对象传入节点对象,而访问者对象则反过来执行节点对象的操作。这样的过程叫做“双重分派”。节点调用访问者,将它自己传入,访问者则将某算法针对此节点执行。

  2.2 访问者模式的结构图

  从上面描述可知,访问者模式是用来封装某种数据结构中的方法。具体封装过程是:每个元素接受一个访问者的调用,每个元素的Accept方法接受访问者对象作为参数传入,访问者对象则反过来调用元素对象的操作。具体的访问者模式结构图如下所示:

  
  这里需要明确一点:访问者模式中具体访问者的数目和具体节点的数目没有任何关系。从访问者的结构图可以看出,访问者模式涉及以下几类角色。

  1)抽象访问者角色(Vistor):声明一个或多个访问操作,使得所有具体访问者必须实现的接口。

  2)具体访问者角色(ConcreteVistor):实现抽象访问者角色中所有声明的接口。

  3)抽象节点角色(Element):声明一个接受操作,接受一个访问者对象作为参数。

  4)具体节点角色(ConcreteElement):实现抽象元素所规定的接受操作。

  5)结构对象角色(ObjectStructure):节点的容器,可以包含多个不同类或接口的容器。

  2.3 访问者模式的实现

  在讲诉访问者模式的实现时,我想先不用访问者模式的方式来实现某个场景。具体场景是——现在我想遍历每个元素对象,然后调用每个元素对象的Print方法来打印该元素对象的信息。如果此时不采用访问者模式的话,实现这个场景再简单不过了,具体实现代码如下所示:

namespace DonotUsevistorPattern
{
    // 抽象元素角色
    public abstract class Element
    {
        public abstract void Print();
    }

    // 具体元素A
    public class ElementA : Element
    {
        public override void Print()
        {
            Console.WriteLine("我是元素A");
        }
    }

    // 具体元素B
    public class ElementB : Element
    {
        public override void Print()
        {
            Console.WriteLine("我是元素B");
        }
    }

    // 对象结构
    public class ObjectStructure
    {
        private ArrayList elements = new ArrayList();

        public ArrayList Elements
        {
            get { return elements; }
        }

        public ObjectStructure()
        {
            Random ran = new Random();
            for (int i = 0; i < 6; i++)
            {
                int ranNum = ran.Next(10);
                if (ranNum > 5)
                {
                    elements.Add(new ElementA());
                }
                else
                {
                    elements.Add(new ElementB());
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ObjectStructure objectStructure = new ObjectStructure();
            // 遍历对象结构中的对象集合,访问每个元素的Print方法打印元素信息
            foreach (Element e in objectStructure.Elements)
            {
                e.Print();
            }

            Console.Read();
        }
    }
}

  上面代码很准确的解决了我们刚才提出的场景,但是需求在时刻变化的。如果此时,我除了想打印元素的信息外,还想打印出元素被访问的时间。此时,我们就不得不去修改每个元素的Print方法,再加入相对应的输入访问时间的输出信息。这样的设计显然不符合“开-闭”原则,即某个方法操作的改变,会使得必须去更改每个元素类。既然,这里变化的点是操作的改变,而每个元素的数据结构是不变的。所以此时就思考——能不能把操作于元素的操作和元素本身的数据结构分开呢?解开这两者的耦合度,这样如果是操作发现变化时,就不需要去更改元素本身了。但是,如果是元素数据结构发现变化,例如,添加了某个字段,这样就不得不去修改元素类了。此时,我们可以使用访问者模式来解决这个问题,即把作用于具体元素的操作由访问者对象来调用。具体的实现代码如下所示:

namespace VistorPattern
{
    // 抽象元素角色
    public abstract class Element
    {
        public abstract void Accept(IVistor vistor);
        public abstract void Print();
    }

    // 具体元素A
    public class ElementA :Element
    {
        public override void Accept(IVistor vistor)
        {
            // 调用访问者visit方法
            vistor.Visit(this);
        }
        public override void Print()
        {
            Console.WriteLine("我是元素A");
        }
    }

    // 具体元素B
    public class ElementB :Element
    {
        public override void Accept(IVistor vistor)
        {
            vistor.Visit(this);
        }
        public override void Print()
        {
            Console.WriteLine("我是元素B");
        }
    }

    // 抽象访问者
    public interface IVistor
    {
        void Visit(ElementA a);
        void Visit(ElementB b);
    }

    // 具体访问者
    public class ConcreteVistor :IVistor
    {
        // visit方法而是再去调用元素的Accept方法
        public void Visit(ElementA a)
        {
            a.Print();
        }
        public void Visit(ElementB b)
        {
            b.Print();
        }
    }

    // 对象结构
    public class ObjectStructure
    {
        private ArrayList elements = new ArrayList();

        public ArrayList Elements
        {
            get { return elements; }
        }

        public ObjectStructure()
        {
            Random ran = new Random();
            for (int i = 0; i < 6; i++)
            {
                int ranNum = ran.Next(10);
                if (ranNum > 5)
                {
                    elements.Add(new ElementA());
                }
                else
                {
                    elements.Add(new ElementB());
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ObjectStructure objectStructure = new ObjectStructure();
            foreach (Element e in objectStructure.Elements)
            {
                // 每个元素接受访问者访问
                e.Accept(new ConcreteVistor());
            }

            Console.Read();
        }
    }
}

  从上面代码可知,使用访问者模式实现上面场景后,元素Print方法的访问封装到了访问者对象中了(我觉得可以把Print方法封装到具体访问者对象中。),此时客户端与元素的Print方法就隔离开了。此时,如果需要添加打印访问时间的需求时,此时只需要再添加一个具体的访问者类即可。此时就不需要去修改元素中的Print()方法了。

  三、访问者模式的应用场景

  每个设计模式都有其应当使用的情况,那让我们看看访问者模式具体应用场景。如果遇到以下场景,此时我们可以考虑使用访问者模式。

  1)如果系统有比较稳定的数据结构,而又有易于变化的算法时,此时可以考虑使用访问者模式。因为访问者模式使得算法操作的添加比较容易。

  2)如果一组类中,存在着相似的操作,为了避免出现大量重复的代码,可以考虑把重复的操作封装到访问者中。(当然也可以考虑使用抽象类了)

  3)如果一个对象存在着一些与本身对象不相干,或关系比较弱的操作时,为了避免操作污染这个对象,则可以考虑把这些操作封装到访问者对象中。

  四、访问者模式的优缺点

  访问者模式具有以下优点:

  1)访问者模式使得添加新的操作变得容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,添加新的操作会变得很复杂。而使用访问者模式,增加新的操作就意味着添加一个新的访问者类。因此,使得添加新的操作变得容易。

  2)访问者模式使得有关的行为操作集中到一个访问者对象中,而不是分散到一个个的元素类中。这点类似与"中介者模式"。

  3)访问者模式可以访问属于不同的等级结构的成员对象,而迭代只能访问属于同一个等级结构的成员对象。

  访问者模式也有如下的缺点:

  1)增加新的元素类变得困难。每增加一个新的元素意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中添加相应的具体操作。

  五、总结

  访问者模式是用来封装一些施加于某种数据结构之上的操作。它使得可以在不改变元素本身的前提下增加作用于这些元素的新操作,访问者模式的目的是把操作从数据结构中分离出来。

参考链接:http://www.cnblogs.com/zhili/p/VistorPattern.html

原文地址:https://www.cnblogs.com/supersnowyao/p/9195833.html

时间: 2024-10-11 11:04:19

【22】访问者模式(Visitor Pattern)的相关文章

访问者模式 Visitor Pattern

访问者模式: 闻词达意,肯定有访问者(对象).接受者(对象).写个实例: 1.IVisitor 抽象访问者角色,声明一个访问操作接口visit(Acceptor ). 2.Visitor 具体访问者角色,实现IVisitor声明的接口. 3.Acceptor 定义一个被访问者,定义接受访问操作(accept(IVisitor)). 4.AcceptorInstance 被访问者实例,实现接受访问操作,然后同意(回调)访问者访问自己. 1 package headfirst.visitor; 2

[设计模式] 23 访问者模式 visitor Pattern

在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问者模式把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化.该模式的目的是要把处理从数据结构分离出来.访问者模式让增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者.访问者模式将有关的行为集中到一个访问者对象中.   初次接触,定义会显得晦涩并且难于理解,没关系,LZ来陪

22.访问者模式(Vistor Pattern)

using System; using System.Collections; namespace ConsoleApplication5 { /// <summary> /// 访问者模式是封装一些施加于某种数据结构之上的操作. /// 一旦这些操作需要修改的话,接受这个操作的数据结构则可以保存不变. /// 访问者模式适用于数据结构相对稳定的系统, /// 它把数据结构和作用于数据结构之上的操作之间的耦合度降低, /// 使得操作集合可以相对自由地改变. /// </summary&

设计模式之访问者模式(Visitor)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式入门之访问者模式Visitor

Set集合的配置 数据表的创建:表关系一个员工拥有多个身份 create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) ); create table CERTIFICATE ( id INT NOT NULL aut

访问者模式 Visitor 行为型 设计模式(二十七)

访问者模式 Visitor <侠客行>是当代作家金庸创作的长篇武侠小说,新版电视剧<侠客行>中,开篇有一段独白: “茫茫海外,传说有座侠客岛,岛上赏善罚恶二使,每隔十年必到中原武林,向各大门派下发放赏善罚恶令, 强邀掌门人赴岛喝腊八粥,拒接令者,皆造屠戮,无一幸免,接令而去者,杳无音讯,生死未仆,侠客岛之行,已被视为死亡之旅.” 不过话说电视剧,我总是觉得老版的好看. 意图 表示一个作用于某对象结构中的各元素的操作. 它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作.

C#设计模式(22)——访问者模式(Vistor Pattern)

一.引言 在上一篇博文中分享了责任链模式,责任链模式主要应用在系统中的某些功能需要多个对象参与才能完成的场景.在这篇博文中,我将为大家分享我对访问者模式的理解. 二.访问者模式介绍 2.1 访问者模式的定义 访问者模式是封装一些施加于某种数据结构之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保存不变.访问者模式适用于数据结构相对稳定的系统, 它把数据结构和作用于数据结构之上的操作之间的耦合度降低,使得操作集合可以相对自由地改变. 数据结构的每一个节点都可以接受一个访问者的调用

访问者模式-Visitor

访问者模式:表示作用于某对象结构的个元素的操作.它使你在不改变各元素的类的前提下定义作用于这些元素的新操作. 访问者模式的适用场景:有比较稳定的数据结构,又有易于变化的算法的话,使用反复问这模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易. 访问者模式的优缺点: 优点:增加新的操作容易,因为增加新的操作就意味着增加一个新的访问者.访问者模式将有关的行为集中到一个访问者对象中. 缺点:访问者模式使得增加新的数据结构变得苦难了. 访问者模式结构图: 代码实现: 1 package com

【设计模式】—— 访问者模式Visitor

对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同.此时,就是访问者模式的典型应用了. 应用场景 1 不同的子类,依赖于不同的其他对象 2 需要对一组对象,进行许多不相关的操作,又不想在类中是现在这些方法 3 定义的类很少改变,但是执行的操作却经常发生改变. 回到顶部 模式结构 Context 环境角色 class Context{ List<Node> list = new ArrayList(); public void add(Node node) { list.ad

设计模式(17) 访问者模式(VISITOR) C++实现

意图: 表示一个作用于某对象结构的各元素的操作.它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作. 动机: 之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式. 在静态分析阶段,将源程序表示为一个抽象语法树,编译器需要在抽象语法树的基础上实施某些操作以进行静态语义分析.可能需要定义许多操作以进行类型检查.代码优化.流程分析.检查变量是否在使用前被赋值,等等. 这个需求的特点是:要求对不同的节点进行不同的处理. 常规设计方法:不同的节点封装不同的操作. 缺点是,节点