【设计模式与游戏完美开发】访问者模式

GoF:“定义一个能够在一个对象结构中对于所有元素执行的操作。访问者让你可以定义一个新的操作,而不必更改到被操作元素的类接口”(一般我打出GoF就说明我不是很理解)

简单来说,访问者是一个考虑了所有“被访问者”可能会需要的接口(功能),被访问者需要放入访问者的它所需的接口中进行处理

处理的问题如下:

假设有Enemy敌人类 和 Solider士兵类 它们均继承于Character基类。

现在需求是统计每一个敌人的防御力求平均值 和 统计每一个士兵的攻击力求平均值 至于求出的数据放哪可以先不管。

一般做法是在士兵类Solider 和 敌人类Enemy中 分别写一个方法来进行处理对应的问题,这样就会改动了2个原有类,违背了开闭原则,而且可能还会提高耦合度。假设又有一个类似的需求出现,那还需要再继续增加原有类接口进行处理!最终原有类会越来越庞大变得难以维护。

用访问者模式就可以避免这种情况,对照这个需求创建一个访问者类VistorA,内部有两个方法如下:

VisitSolider(List<Solider> soliderList){  ...   }

VisitEnemy(List<Enemy> enemyList){  ...  }

调用时: VistorA  va = new VistorA();

va.VisitSolider( GetSoliderList() );//GetSoliderList()获取所有士兵

va.VisitEnemy( GetEnemyList() );//GetEnemyList()获取所有敌人

假设又有一个类似的需求,需要访问原有类Enemy或Solider ,而且也同样是访问所有士兵和敌人,此时你就可以再创一个VistorB来处理这个需求,而VistorA和VistorB是十分类似的,为此我们可以写一个接口Vistor来进行抽象化,每一个需求VistorX访问者都要继承于Vistor,让实现和调用分离开来,从而降低耦合(这就是面向接口编程思想)

变为如下:

Vistor vistor = new VistorA()

vistor.VistorSolider(soliderList)

vistor.VistorEnemy(enemyList)

假设要转为 新需求VistorB的方式,代码只需改动上面的VistorA变为VistorB就OK了!因为A和B都是实现Vistor接口的方法,其方法名和方法所需参数是一样的,这就保证了我们只需要改动一个地方就OK了,当然上面这种举例还是太浅了,等到具体遇到项目问题时,就会理解面向接口的好处(或者说是面向抽象的好处)


2020年6月22日02:16:56更新  比上方的例子稍微复杂点的例子,可以对比着2种的访问模式,都是适用于不同需求而言的,没有根本上的利弊之分,我认为这些操作都是具体和抽象之间的转化而已,下面例子是被访问者的方法调用交给访问者进行。

public interface IVistor
{
   void VisitA(BeVisitA beVisit); 
   void VisitB(BeVisitB beVisit); 
}

public class FirstVistor : IVistor
{
  //(4) [依赖具体调用] 执行被访问者A的具体方法
  public void VisitA(BeVisitA beVisit)
  {
     beVisit.Run();  
  }
  public void VisitB(BeVisitB beVisit)
  {
     beVisit.Sleep();  
  }
}
public class SecondVistor : IVistor
{
  //其实IVistor可以只写一个接口Visit(IBeVisit beVisit),
  //不过要写if判断和强转对应类型进行调用具体类方法。如 if(beVisit is BeVisitA){ ((BeVisitA)beVisit).Walk();  } 
  //这会消耗一定性能
  public void VisitA(BeVisitA beVisit)
  {
     beVisit.Walk();  
  }
  public void VisitB(BeVisitB beVisit)
  {
     beVisit.Look();  
  }
}

public interface IBeVisit
{
   void VisitedByWho(IVistor vistor);
}
public class BeVisitA : IBeVisit
{
   //实现接口方法,外部遍历调用IBeVisit接口时进入到此,从抽象变成具体调用,然后再变成抽象调用
   public void VisitedByWho(IVistor vistor)
   {
       //(3)[抽象调用] 访问者VisitA接口方法,传递被访问者this,由具体访问者进行访问this
       vistor.VisitA(this);
       //假设我直接在这里调用Run() 或 Walk()方法,访问者的意义就不存在了
       //访问者意义在于,选择执行哪个具体方法,比如FirstVistor选择执行Run,SecondVistor选择执行Walk,
        //当然你也可以弄个ThirdVistor执行Run()和Walk()。
       //很明显,如果你没有vistor辅助,你需要新增BeVisitA接口,破坏内部封闭原则。
   }
   public void Run()
   {
      //...
   }
   public void Walk()
   {
      //...
   }
}
public class BeVisitB : IBeVisit
{
   public void VisitedByWho(IVistor vistor)
   {
       vistor.VisitB(this);
   }
   public void Sleep()
   {
      //...
   }
   public void Look()
   {
      //...
   }
}

void Main()
{
    List<IBeVisit> list = new List<IBeVisit>();//(1)依赖倒置[抽象]
    list.Add(new BeVisitA());
    list.Add(new BeVisitB());
    
    IVistor vistor = new FirstVistor();
    foreach(var v in list)
    {
        //(2)[抽象调用]调用IBeVisit接口方法VisitedByWho,由Visitor访问者决定如何进行调用v的方法
        v.VisitedByWho(vistor);    
    }
}

上方例子注释(1)、(2)、(3)、(4)按顺序看,整个调用流程,抽象->(具体)->抽象->具体,  第二步的具体是指从调用IBeVisit接口方法VisitedByWho,程序转到具体的IBeVisit接口实现类进行,其他的应该都很好懂。

思考:将旧例子Enemy和Solider魔改,但依然保持一部分会如何,需求改变为访问所有角色(敌人和士兵)调用它们的共同角色接口DoSomething()。看看会出现啥问题。

public interface ICharacter
{
   void DoSomething();
}

public class Enemy : ICharacter
{
   public void DoSomething()
   {
       Fly();
   }

   public void Fly() 
   {
     //...
   }
}
public class Solider : ICharacter
{
   public void DoSomething()
   {
       Run();
   }

   public void Run() 
   {
     //...
   }
}

public interface IVistor
{
   void VisitAll(List<ICharacter> list);
}


public class FirstVistor : IVistor
{
   public void VisitAll(List<ICharacter> list)
   { 
       foreach(var v in list)
       {
           v.DoSomething();
           //写到这里时我突然发现,是的,这个接口无意义,vistor没有起到任何作用
           //除非这个访问者还进行了其他特别的操作 如:调用v.DoSomething之后,假设DoSomething
           //有返回值bool, 判断bool是否为false,为false则继续执行其他操作。如此一来就有了变化
       }
   }
}

void Main()
{
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 设计模式游戏完美开发 PDF 是两个不同的东西,其中设计模式游戏是一本介绍常用设计模式的书籍,而完美开发 PDF 是一种开发技巧与知识总结的文档。 设计模式游戏适合那些想更深入了解设计模式的软件工程师、程序设计师或学习设计模式的学生。这本书详细介绍了23种常见的设计模式,并通过游戏方式帮助读者更好地理解和掌握这些模式的应用。设计模式可以提高代码的可读性、可维护性和可扩展性,是面向对象编程的重要思想。 完美开发 PDF 则适合那些想提升自己开发技巧和知识的人员。这份文档详细总结了一些软件开发中的注意事项和技巧,如代码规范、调试技巧、内存管理、测试等。这些知识和技巧可以帮助开发人员写出高质量、高效率的代码,提高开发效率和质量。 总的来说,这两个东西都是非常有价值的,但需要针对自己的需求选择合适的。设计模式游戏可以帮助读者掌握常用的设计模式,提高自己的编程思维和能力,而完美开发 PDF 则可以帮助读者提高开发能力和效率,写出更加高质量的代码。 ### 回答2: 设计模式游戏是一个基于游戏框架的学习设计模式的互动式教程。该游戏涵盖了常见的设计模式,通过解决问题,并用代码实现来学习设计模式游戏中的角色扮演和动画效果增加了游戏的趣味性和互动性,从而更容易留下深刻的理解。设计模式游戏是一个非常有效的学习工具,使得学习设计模式更加生动和有趣。 而完美开发 pdf 则是一本以实践为主的技术书籍,它详细介绍了软件开发中的各个环节,包括需求分析、设计、代码实现、测试、维护等。该书以项目驱动的方式,给出了概念性介绍和实际的代码实现,使得读者可以了解到整个软件开发流程,并获得实际的代码编写技巧和实践经验。完美开发 pdf 是一本非常实用的技术书籍,适合那些想要学习软件开发和完整项目开发的人员。 两者相比较,设计模式游戏更注重提高学习者的兴趣和理解,而完美开发 pdf 更注重实际操作和项目实践,两者均是非常有效的学习工具和实践指南,适合不同层次的软件工程师和学习者。 ### 回答3: 设计模式游戏是一款面向软件开发者的教育游戏,其目的是通过互动式的游戏流程,让学习者深入了解和掌握设计模式的原理、应用和实现。通过该游戏的教育模式,学习者可在不断试错和调整的过程中,逐渐领会设计模式的实际意义和作用,有效提升自身的软件设计和开发能力。相比传统的讲授式教育模式,设计模式游戏更注重学习者的实践和探索,提升学员的参与度和自主学习能力。 而完美开发 pdf 则是一本软件开发者必备的技术读物,它以设计模式为基础,深入解析了软件开发的各个方面,涉及到的知识点非常全面而深入。它主要从实践的角度出发,以案例为驱动,通过实例和代码的演示,让读者系统地了解和掌握软件设计和开发中的常见问题和解决方案。作为一本非常经典的开发读物,完美开发 pdf 具有指导性和启发性,可让软件开发者在实践中逐渐成长和完善自身的工作和技能。 因此,通过设计模式游戏完美开发 pdf 的学习,软件开发者可在掌握设计模式的同时,更全面地了解和掌握软件开发的各个方面,从而在实践中不断成长和提升自身的技术水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值