设计模式深入学习--Visitor 访问者模式(行为型模式)

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

//Visitor类 为该对象结构中ConcreteElement的每一个类声明一个Visit操作
 abstract class Visitor
 {
     public abstract void VisitConcreteElementA(ConcreteElementA concreteElementa);
 
     public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
 }
 
 
 //具体访问者 实现每个由Visitor声明的操作 每个操作实现算法的一部分 而该算法片断是对应结构类中对象的类
 class ConcreteVisitor1 : Visitor
 {
     public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
     {
         Console.WriteLine(concreteElementA.GetType().Name+"被"+this.GetType().Name+"访问");
     }
 
     public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
     {
         Console.WriteLine(concreteElementB.GetType().Name + "被" + this.GetType().Name + "访问");
     }
 }
 
 //具体访问者 实现每个由Visitor声明的操作 每个操作实现算法的一部分 而该算法片断是对应结构类中对象的类
 class ConcreteVisitor2 : Visitor
 {
     public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
     {
         Console.WriteLine(concreteElementA.GetType().Name + "被" + this.GetType().Name + "访问");
     }
 
     public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
     {
         Console.WriteLine(concreteElementB.GetType().Name + "被" + this.GetType().Name + "访问");
     }
 }
 
 //Element类 定义一个Accept操作 它以一个访问者为参数
 abstract class Element
 {
     public abstract void Accept(Visitor visitor);
 }
 
 class ConcreteElementA : Element
 {
     public override void Accept(Visitor visitor)
     {
         visitor.VisitConcreteElementA(this);
     }
 
     //其他相关方法
     public void OperationA()
     {
          
     }
 }
 
 
 class ConcreteElementB : Element
 {
     public override void Accept(Visitor visitor)
     {
         visitor.VisitConcreteElementB(this);
     }
 
     //其他相关方法
     public void OperationB()
     {
 
     }
 }
 
 
 class ObjectStructures
 {
     private List<Element> elementList=new List<Element>();
 
     public void Attach(Element element)
     {
         elementList.Add(element);
     }
 
     public void Detach(Element element)
     {
         elementList.Remove(element);
     }
 
     public void Accept(Visitor visitor)
     {
         foreach (Element  e in elementList)
         {
             e.Accept(visitor);
         }
     }
 
 }
    首先声明两个虚拟类 Visitor虚拟访问者类和Element虚拟元素类,然后在构造两个Visitor虚拟访问类的子类ConcreteVisitor1和ConcreteVisitor2,这两个类都重写父类方法,并有全部传入Element的子类的重载方法,也就是说Element有几个子类Visitor的子类就有几个方法,全部都传入这些子类做为参数。  然后再构造两个Element的子类ConcreteElementA和ConcreteElementB,这些子类重写父类方法Accept()并有一个 Visitor的参数。 大概就是这些,其实就是两个父类之间,互相有耦合,子类都离不开对方的类。最后就是一个对象结构类ObjectStructures,类似一个管理器,把元素类Element都管理起来, 再来看看实现方法:
ObjectStructures os=new ObjectStructures();
      //声明两个状态
      os.Attach(new ConcreteElementA());
      os.Attach(new ConcreteElementB());
 
      //相对不变 稳定的类
      ConcreteVisitor1 c1=new ConcreteVisitor1();
 
      ConcreteVisitor2 c2 = new ConcreteVisitor2();
     //传入状态
      os.Accept(c1);
      os.Accept(c2);

   结果就正确输出了,我们再来理一遍V isitor 访问者模式。该设计模式适用于有两组都很多元素需要操作,且一边元素相对稳定,为了避免我们重复写很多代码,就可以用Visitor 访问者模式,我们再来个小例子更好的理解下该模式用法, 我们要做的是一个多人游戏对战中,里面含有不同的人物,然后也有几种的不同的Buff,不同的人物吃到Buff都会有不一样的效果,比如我们的加血Buff我方英雄吃到就会加血,敌军吃到就会减血,而小兵吃到就不会有任何反应。

//抽象Buff
   public abstract class Buff
   {
       protected string name { get; set; }
       protected string effect { get; set; }
       public Buff(string name, string effect)
       {
           this.name = name;
           this.effect = effect;
       }
 
       public string GetName()
       {
           return name;
       }
 
       public string GetEffect()
       {
           return effect;
       }
 
       public abstract void Accept(Warriors visitor);
   }
 
   public class RedBuff : Buff
   {
       public RedBuff(string name, string effect) : base(name, effect)
       {
       }
 
       public override void Accept(Warriors visitor)
       {
           visitor.visitors(this);
       }
   }
   public class BlueBuff : Buff
   {
       public BlueBuff(string name, string effect) : base(name, effect)
       {
       }
 
       public override void Accept(Warriors visitor)
       {
           visitor.visitors(this);
       }
   }
   public class GreenBuff : Buff
   {
       public GreenBuff(string name, string effect) : base(name, effect)
       {
       }
 
       public override void Accept(Warriors visitor)
       {
           visitor.visitors(this);
       }
   }
 
   //抽象访问者
   public abstract class Warriors
   {
       protected string name { get; set; }
 
       public Warriors(string name)
       {
           this.name = name;
       }
 
       public abstract void visitors(RedBuff rBuff);
 
       public abstract void visitors(BlueBuff bBuff);
 
       public abstract void visitors(GreenBuff gBuff);
 
   }
 
   //具体访问者  英雄大魔法师
   public class Hero : Warriors
   {
       public Hero(string name) : base(name)
       {
       }
 
       public override void visitors(RedBuff rBuff)
       {
           Console.WriteLine("大魔法师 " + this.name + "吃到" + rBuff.GetName() + " 功能是:" + rBuff.GetEffect());
       }
 
       public override void visitors(BlueBuff bBuff)
       {
           Console.WriteLine("大魔法师 " + this.name + "吃到" + bBuff.GetName() + " 功能是:" + bBuff.GetEffect());
       }
 
       public override void visitors(GreenBuff gBuff)
       {
           Console.WriteLine("大魔法师 " + this.name + "吃到" + gBuff.GetName() + " 功能是:" + gBuff.GetEffect());
       }
   }
 
   //具体访问者 敌人Boss
   public class Enemy : Warriors
   {
       public Enemy(string name) : base(name)
       {
       }
 
       public override void visitors(RedBuff rBuff)
       {
           Console.WriteLine("敌方Boss " + this.name + "吃到" + rBuff.GetName() + " 反而让敌方Boss减攻击力");
       }
 
       public override void visitors(BlueBuff bBuff)
       {
           Console.WriteLine("敌方Boss " + this.name + "吃到" + bBuff.GetName() + " 反而让敌方Boss减蓝");
       }
 
       public override void visitors(GreenBuff gBuff)
       {
           Console.WriteLine("敌方Boss " + this.name + "吃到" + gBuff.GetName() + " 反而让敌方Boss减血");
       }
   }
 
   //具体访问者  玩家
   public class Player : Warriors
   {
       public Player(string name) : base(name)
       {
       }
 
       public override void visitors(RedBuff rBuff)
       {
           Console.WriteLine("玩家player " + this.name + "吃到" + rBuff.GetName() + " 功能是:" + rBuff.GetEffect());
       }
 
       public override void visitors(BlueBuff bBuff)
       {
           Console.WriteLine("玩家player " + this.name + "吃到" + bBuff.GetName() + " 功能是:" + bBuff.GetEffect());
       }
 
       public override void visitors(GreenBuff gBuff)
       {
           Console.WriteLine("玩家player " + this.name + "吃到" + gBuff.GetName() + " 功能是:" + gBuff.GetEffect());
       }
   }
 
   //具体访问者 小兵
   public class Soldiers : Warriors
   {
       public Soldiers(string name) : base(name)
       {
       }
 
       public override void visitors(RedBuff rBuff)
       {
           Console.WriteLine("小兵 " + this.name + "吃到" + rBuff.GetName() + "小兵不是英雄,吃了没反应");
       }
 
       public override void visitors(BlueBuff bBuff)
       {
           Console.WriteLine("小兵 " + this.name + "吃到" + bBuff.GetName() + "小兵不是英雄,吃了没反应");
       }
 
       public override void visitors(GreenBuff gBuff)
       {
           Console.WriteLine("小兵 " + this.name + "吃到" + gBuff.GetName() + "小兵不是英雄,吃了没反应");
       }
   }
 
   public class BuffManager
   {
       private List<Buff> buffList = new List<Buff>();
 
       public void Accpet(Warriors visitors)
       {
           foreach (var item in buffList)
           {
               item.Accept(visitors);
           }
       }
 
       public void Add(Buff med)
       {
           buffList.Add(med);
       }
 
       public void Remove(Buff med)
       {
           buffList.Remove(med);
       }
   }
    首先构造两个虚拟类,一个是抽象元素Buff类,一个是抽象访问者类Warriors。 我们的游戏中,我们可以假设人物是可以变化的,每次进入游戏玩家选择的英雄都不一定相同,所以战士抽象类就是我们前说的访问者类 也就是易变化的类,比如很多游戏就会不断推出新的英雄。而Buff在一场游戏下来相对稳定,不会说明天就取消这个Buff功能,后天又加入其它Buff功能,这样的情况只有项目经理才是易于变化的。 所以现在我们把不易变化的Buff类设为元素类,易于变化的Warriors作为访问者类,后期就算Warriors不断增加新人物,只要Buff不变我们的游戏就还是可以很方便更新功能的。现在继续再构造3个Buff吧 红Buff,蓝Buff,绿Buff,各自都有各自的功能,都重写父类Accept方法,并传入我们的访问者类Warriors,然后在输出Warriors的visitors方法。 接下来构造几个实体访问者类Warriors的子类,也就是游戏人物类。 一个Hero类,代表我们的第三方英雄,重写父类的visitors方法,和我们介绍的一样,Buff类有几个子类,我们就需要实现几个重载方法,用来处理各个不同的Buff情况,所以这里就需要重载3个不同的buff传参方法。 各自实现对不同的buff处理情况。 后面的Enemy类 敌人类,Player类玩家类,Soldiers类小兵类也是如此,各自继承Warriors类,重写重载各自对buff的处理方法。 好了,最后在来一个BuffManager Buff管理器,里面管理我们的全部Buff。 代码和介绍基本完成了,现在看看实现代码:
//Buff类型
         Buff RedBuff = new RedBuff("力量光环", "增加周围友军战斗力");
         Buff BlueBuff = new BlueBuff("魔法光环", "提升周围友军魔法恢复速度");
         Buff GreenBuff = new GreenBuff("生命光环", "提升周围友军生命恢复速度");
         //生命管理器,管理全部buff
         BuffManager buffManager = new BuffManager();
         buffManager.Add(RedBuff);
         buffManager.Add(BlueBuff);
         buffManager.Add(GreenBuff);
 
         //加入战场角色
         Warriors hero = new Hero("甘道夫");
         Warriors enemy = new Enemy("魔神赵日天");
         Warriors zhazha = new Soldiers("小兵");
         Warriors player = new Player("玩家");
 
         //中立英雄甘道夫吃到Buff
         buffManager.Accpet(hero);
         //敌人Boss吃到Buff
         buffManager.Accpet(enemy);
         //小兵渣渣吃到Buff
         buffManager.Accpet(zhazha);
         //玩家吃到Buff
         buffManager.Accpet(player);

   首先声明3个Buff和各自作用,加入Buff管理器,初始化我们的Buff功能, 然后声明几个游戏人物,最后模拟下每个人物吃到Buff的情况,就一句代码  buffManager.Accpet(hero); 把游戏人物传入Buff管理器中,就可以了,现在看出这个设计模式的好处了吗,对于我们的访问者类 游戏人物 来说,不管增加还是减少了几个,都可以很容易的添加删除修改,当然 前提是Buff不变,如果Buff变动了其实这个设计模式就不是很好用了。再来总结下Visitor 访问者模式的应用场景,如果系统有比较稳定的数据结构,而又有易于变化的算法时,此时可以考虑使用访问者模式。因为访问者模式使得算法操作的添加比较容易。如果一组类中,存在着相似的操作,为了避免出现大量重复的代码,可以考虑把重复的操作封装到访问者中。(当然也可以考虑使用抽象类了)如果一个对象存在着一些与本身对象不相干,或关系比较弱的操作时,为了避免操作污染这个对象,则可以考虑把这些操作封装到访问者对象中。
   Visitor 访问者模式的优缺点
   Visitor 访问者模式具有以下优点:
         Visitor 访问者模式使得添加新的操作变得容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,添加新的操作会变得很复杂。而使用访问者模式,增加新的操作就意味着添加一个新的访问者类。因此,使得添加新的操作变得容易。
Visitor 访问者模式使得有关的行为操作集中到一个访问者对象中,而不是分散到一个个的元素类中。这点类似与”中介者模式”。
Visitor 访问者模式可以访问属于不同的等级结构的成员对象,而迭代只能访问属于同一个等级结构的成员对象。
Visitor 访问者模式也有如下的缺点:
增加新的元素类变得困难。每增加一个新的元素意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中添加相应的具体操作。
   总结
     Visitor 访问者模式是用来封装一些施加于某种数据结构之上的操作。它使得可以在不改变元素本身的前提下增加作用于这些元素的新操作,访问者模式的目的是把操作从数据结构中分离出来。
      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值