通俗易懂设计模式解析——访问者模式

前言

  今天咱们看的是访问者模式【Visitor Pattern 】,咱们理解访问者模式这个名称可能会有利于咱们理解其核心代码块。咱们看这么个例子:我去朋友家作客,那么朋友属于主人,我则属于访问者。这时恰好朋友在炒菜,却没得酱油了。若是朋友下去买酱油将会很麻烦并且会影响炒菜。这时就到我这个访问者出马了。一溜烟的出去打着酱油就回来了。简单理解的来讲就是,访问者在主人原来的基础上帮助主人去完成主人不方便或者完不成的东西。html

访问者模式介绍

1、来由

  在软件系统开发中,咱们常常会碰见一些层次结构无缺的代码由于需求的更改而更改。你说这个时候我更改其基类吧,全部子类都要更改、这是一件很麻烦的事情。那么我能不能在不修改器层次结构完整的前提下完成新的需求的更改呢?设计模式

2、意图

  表示一个做用于某对象结构中的各个元素的操做。它能够在不改变各元素的类的前提下定义做用于这些元素的新的操做。数据结构

3、案例图

4、访问者模式代码示例

看上面案例图能够发现访问者模式包含如下部分:ide

结构对象:节点的容器,包含多个类或者接口
抽象节点:声明一个接收操做,接收访问者对象做为参数,声明处理接口,处理节点数据this

具体节点:实现抽象节点的接收操做和处理操做spa

抽象访问者:声明一个或多个访问操做,使得全部具体访问者都必须实现设计

具体访问者:实现抽象访问者声明的接口code

咱们看这么一个需求,咱们须要计算图形的面积并输出、图形包括长方形圆形。咱们一块儿看看代码实现吧:htm

  首先咱们看不采用访问者模式实现:对象

namespace Visitor_Pattern
{
    class VisitorPattern
    {
    }

    /// <summary>
    /// 抽象节点类
    /// </summary>
    public abstract class Element 
    {
       public abstract void CalculatedArea();
    }

    /// <summary>
    /// 长方形计算面积输出
    /// </summary>
    public class Rectangle : Element
    {
        public double _long;
        public double _wide;

        public  Rectangle(double Long, double Wide) 
        {
            this._long = Long;
            this._wide = Wide;
        }
        public override void CalculatedArea()
        {
            Console.WriteLine($"长方形面积是:{_long*_wide}");
        }
    }

    /// <summary>
    /// 圆形计算面积输出
    /// </summary>
    public class Circular : Element
    {
        public double _r; 

        public Circular(double r)
        {
            this._r = r; 
        }
        public override void CalculatedArea()
        {
            Console.WriteLine($"圆形面积是:{Math.PI * _r*_r}");
        }
    }

    /// <summary>
    /// 结构对象
    /// </summary>
    public class Graphical 
    {
        public List<Element> elements = new List<Element>();
        public List<Element> Elements
        {
            get { return elements; }
        }

        public Graphical() 
        {
            Element element = new Rectangle(10,5);
            Element element1= new Circular(5);
            elements.Add(element);
            elements.Add(element1);
        }
    }
}

 

namespace Visitor_Pattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Graphical graphical = new Graphical();
            foreach (var item in graphical.Elements)
            {
                item.CalculatedArea();
            }

        }
    }
}

 

  咱们能够看到实现起来仍是较为简单的。可是若是这里需求进行了变更,须要加上图形的周长而且输出、同时输出其参数。这就麻烦了。就须要对基类进行更改而后修改子类。就感受有点得不偿失了。

  咱们再看采用模式实现:

namespace Visitor_Pattern
{
    class UseVisitorPattern
    {
    }

    #region 访问者

    /// <summary>
    /// 抽象访问者
    /// </summary>
    public interface IVistor 
    {
        void Visit(UseRectangle rectangle);
        void Visit(UseCircular  useCircular);
    }

    /// <summary>
    /// 具体访问者
    /// </summary>
    public class Vistor : IVistor
    {
        public void Visit(UseRectangle rectangle)
        {
            rectangle.CalculatedArea();
            Console.WriteLine($"长方形长是:{rectangle._long}");
            Console.WriteLine($"长方形宽是:{rectangle._wide}");
            Console.WriteLine($"长方形周长是:{2*(rectangle._long+rectangle._wide)}");
        }

        public void Visit(UseCircular useCircular)
        {
            useCircular.CalculatedArea();
            Console.WriteLine($"圆形的半径是:{useCircular._r}");
            Console.WriteLine($"圆形的周长是是:{2*Math.PI*useCircular._r}");
        }
    }

    #endregion

    #region 节点类

    /// <summary>
    /// 抽象节点类
    /// </summary>
    public abstract class UseElement
    {
        public abstract void Accept(IVistor vistor);
        public abstract void CalculatedArea();
    }

    /// <summary>
    /// 长方形计算面积输出
    /// </summary>
    public class UseRectangle : UseElement
    {
        public double _long;
        public double _wide;

        public UseRectangle(double Long, double Wide)
        {
            this._long = Long;
            this._wide = Wide;
        }

        public override void Accept(IVistor vistor)
        {
            vistor.Visit(this);
        }

        public override void CalculatedArea()
        {
            Console.WriteLine($"长方形面积是:{_long * _wide}");
        }
    }

    /// <summary>
    /// 圆形计算面积输出
    /// </summary>
    public class UseCircular : UseElement
    {
        public double _r;

        public UseCircular(double r)
        {
            this._r = r;
        }
        public override void Accept(IVistor vistor)
        {
            vistor.Visit(this);
        }
        public override void CalculatedArea()
        {
            Console.WriteLine($"圆形面积是:{Math.PI * _r * _r}");
        }
    }

    #endregion

    /// <summary>
    /// 结构对象
    /// </summary>
    public class UseGraphical
    {
        public List<UseElement> elements = new List<UseElement>();
        public List<UseElement> Elements
        {
            get { return elements; }
        }

        public UseGraphical()
        {
            UseElement element = new UseRectangle(10, 5);
            UseElement element1 = new UseCircular(5);
            elements.Add(element);
            elements.Add(element1);
        }
    }
}

 

namespace Visitor_Pattern
{
    class Program
    {
        static void Main(string[] args)
        {

            UseGraphical graphical = new UseGraphical();
            foreach (var item in graphical.Elements)
            {
                item.Accept(new Vistor());
            }
        }
    }
}

  这里咱们对每一个节点都加入了访问者,这样咱们需求变更增长周长和参数的输出的时候修改增长具体访问者就能够实现了。

使用场景及优缺点

1、使用场景

一、对象结构中对象对应的类较少改变、可是会常常在此对象结构上定义新的操做

二、须要对一个对象结构中进行一些不相关的操做、须要在新增操做时避免改变其原来的类

2、优势

一、符合单一职责原则。每一个类负责一个职责

二、具备优秀的扩展性和灵活性、添加新的操做会变得较为容易、同时也不会改变其原来的结构代码

三、访问者模式将一些相关的行为操做集合在了访问者对象中,并无分散在其元素类中

3、缺点

一、具体元素对访问者公开了细节,违背了迪米特原则

二、增长具体元素节点变得困难、与之随之增长的就是在访问者中新增。

总结

  访问者模式就介绍到这里啦。访问者模式主要是将数据结构及操做分离、解决了稳定的数据结构和容易变化的操做的耦合性。在新增操做的时候不修改原来的结构对象的类。直接修改访问者对象中的操做便可新增操做。


      若是咱们想要更多的玫瑰花,就必须种植更多的玫瑰树。

     C#设计模式系列目录

        欢迎你们扫描下方二维码,和我一块儿踏上设计模式的闯关之路吧!

  

相关文章
相关标签/搜索