用心理解设计模式——访问者模式 (Visitor Pattern)

前置文章: 设计模式的原则 

其他设计模式:用心理解设计模式

设计模式相关代码已统一放至 我的 Github

一、定义

  行为型模式之一。

  Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

  (表示要对对象结构的元素执行的操作,访问者模式能够让你定义一个新的操作,而无需改变它所操作的元素所在的类。)

二、结构解析

  访问者模式的一般结构有五种角色: 对象结构、抽象元素、具体元素、抽象访问者、具体访问者。

  对象结构(ObjectStructure):一个稳定的数据模型(要被访问操作的元素所在的类),包含一些元素(元素个数总是不变),并实现了对这些元素进行操作的方法,在这些操作方法中,完成了具体元素对访问者的接受(接受后即反调,所以也可以说是 “完成访问者对元素的访问”)。

扫描二维码关注公众号,回复: 5938639 查看本文章

  抽象元素(Element):定义一个用于接受访问者的接口,并要求子类在该接口的实现里,最终反调访问者对元素进行访问操作的接口,达成访问者访问元素的目的。

  具体元素(ConcreteElement):实现抽象元素定义的接口。

  抽象访问者(Visitor):定义访问者对每个具体元素的访问操作接口。

  具体访问者(ConcreteVisitor):实现抽象访问者定义的接口。

三、评价

  访问者模式,将数据模型的操作实现放到了访问者中,使数据模型变得稳定不变、对模型中元素的操作变得易于扩展。

  它的核心思想请参考 消除程序中的 if else(二),实际就是使用了 策略模式,即,访问者自带访问策略。

  对比使用访问者模式前后的访问过程:

  使用前,访问者调用对象结构中用于访问元素的方法,在该方法内,根据访问者的不同,对元素进行不同的访问操作。(这种方式,访问操作实现在对象结构中,对象结构耦合了各个具体访问者,导致增加访问者就要修改对象结构,违背开闭原则

  使用后,访问者自备 “对元素的操作方法” 去访问对象结构中的元素,被访问元素接受访问者,并反调访问者自备的“对元素的操作方法”。(这种方式,访问操作实现在访问者中,对象结构依赖的是抽象访问者,增加具体访问者不用修改对象结构,满足开闭原则

四、实现

using UnityEngine;

namespace Visitor
{
    //对象结构
    public class ObjectStructure
    {
        private ConcreteElementX elementX;
        private ConcreteElementY elementY;

        public ObjectStructure()
        {
            elementX = new ConcreteElementX();
            elementY = new ConcreteElementY();
        }

        public void VisitElementX(Visitor visitor)
        {
            //原来,在每次增加新的访问者时,都必须修改该方法,增加一个If和相应的访问操作逻辑。
            //if (visitor is ConcreteVisitorA)
            //{
            //    Debug.Log("ConcreteVisitorA 访问 ElementX");
            //}
            //else if (visitor is ConcreteVisitorB)
            //{
            //    Debug.Log("ConcreteVisitorB 访问 ElementY");
            //}

            // 现在总是这一句,保证了ObjectStructur类不因增加新的访问者(新的访问者对应新的操作)而被修改!
            // 访问者对元素的访问操作逻辑,被放到了访问者中去扩展。
            elementX.Accept(visitor);
        }

        public void VisitElementY(Visitor visitor)
        {
            elementY.Accept(visitor);
        }
    }

    //抽象元素
    public abstract class Element
    {
        public abstract void Accept(Visitor visitor);
    }

    //具体元素X
    public class ConcreteElementX : Element
    {
        public override void Accept(Visitor visitor)
        {
            visitor.VisitElementX(this);
        }
    }

    //具体元素Y
    public class ConcreteElementY : Element
    {
        public override void Accept(Visitor visitor)
        {
            visitor.VisitElementY(this);
        }
    }

    //抽象访问者
    public abstract class Visitor
    {
        //访问元素的接口,这里可以有两种写法。 
        //这两种写法都不能解决扩展“元素”时对“访问者”的修改问题(事实上该设计模式,就是在Element稳定的基础上进行的)。
        //推荐第二种(养成习惯,面向抽象编程和接口编程,尽可能将 已确定/不变 部分放在抽象类中)

        //写法1、在抽象 Visitor中 定义一个唯一访问接口,然后在每个ConcreteVisitor的实现中对每个元素进行区分对待。
        //public abstract void VisitElement(Element element);

        //写法2、在抽象 Visitor中 针对不同元素,定义不同的访问接口 VisitElementX、VisitElementY。
        public abstract void VisitElementX(ConcreteElementX element);
        public abstract void VisitElementY(ConcreteElementY element);
    }

    //具体访问者A
    public class ConcreteVisitorA : Visitor
    {
        public override void VisitElementX(ConcreteElementX element)
        {
            Debug.Log("ConcreteVisitorA 访问 ElementX");
        }

        public override void VisitElementY(ConcreteElementY element)
        {
            Debug.Log("ConcreteVisitorA 访问 ElementY");
        }
    }

    //具体访问者B
    public class ConcreteVisitorB : Visitor
    {
        public override void VisitElementX(ConcreteElementX element)
        {
            Debug.Log("ConcreteVisitorB 访问 ElementX");
        }

        public override void VisitElementY(ConcreteElementY element)
        {
            Debug.Log("ConcreteVisitorB 访问 ElementY");
        }
    }

    //客户
    public class Client
    {
        static public void Main()
        {
            ObjectStructure objectStructur = new ObjectStructure();
            ConcreteVisitorA concreteVisitorA = new ConcreteVisitorA();
            ConcreteVisitorB concreteVisitorB = new ConcreteVisitorB();

            objectStructur.VisitElementX(concreteVisitorA);
            objectStructur.VisitElementX(concreteVisitorB);

            objectStructur.VisitElementY(concreteVisitorA);
            objectStructur.VisitElementY(concreteVisitorB);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/NRatel/article/details/84672863
今日推荐