通俗易懂設計模式解析——訪問者模式

前言

  今天咱們看的是訪問者模式【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#設計模式系列目錄

        歡迎你們掃描下方二維碼,和我一塊兒踏上設計模式的闖關之路吧!

  

相關文章
相關標籤/搜索