你什么时候应该真正使用访客模式 [英] When should you really use the visitor pattern

查看:182
本文介绍了你什么时候应该真正使用访客模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在将这个标记为重复之前,请先确认一下。
我正在阅读访问者模式及其适用的用途。



我曾经偶然发现这个帖子:



现在,我想介绍一个操作 - 在移动设备上打开蓝牙。



其功能签名应该像这样 -

  void SwitchOnBlueTooth (IMobileDevice mobileDevice,IBlueToothRadio blueToothRadio)

所以根据正确的设备类型根据蓝牙无线电的正确类型,它可以通过调用适当的步骤或算法开启。



原则上,它成为一个3 x 2矩阵,其中 - 我正在尝试根据所涉及的对象的正确类型向量权限。



根据两个参数的类型,可能会出现多态。





现在,我将介绍访问者模式来解决这个问题。灵感来自维基百科页面 - 实质上,访问者可以将一个新的虚拟函数添加到一个类中,而无需修改类;相反,一个创建一个访问者类,实现虚拟函数的所有相应的专门化。访客将实例引用作为输入,并通过双重分派来实现目标。



由于3x2矩阵



在代码中介绍访客模式 -



我必须首先做出决定,类层次结构更加稳定(不太容易发生变化) - 设备类层次结构或蓝牙类层次结构。
一个更稳定的将成为可访问的类&不太稳定的人将成为访客类。对于这个例子,我会说设备类更稳定。



这是设置



这里是客户端代码&测试代码

  class Client 
{
public void SwitchOnBlueTooth(IMobileDevice mobileDevice,IBlueToothVisitor blueToothRadio)
{
mobileDevice.TurnOn(blueToothRadio);
}
}


[TestClass]
public class VisitorPattern
{

客户端mClient =新客户端();

[TestMethod]
public void AndroidOverBroadCom()
{
IMobileDevice device = new Android();
IBlueToothVisitor btVisitor = new BroadComBlueToothVisitor();

mClient.SwitchOnBlueTooth(设备,btVisitor);
}

[TestMethod]
public void AndroidOverIntel()
{
IMobileDevice device = new Android();
IBlueToothVisitor btVisitor = new IntelBlueToothVisitor();

mClient.SwitchOnBlueTooth(设备,btVisitor);
}

[TestMethod]
public void iPhoneOverBroadCom()
{
IMobileDevice device = new iPhone();
IBlueToothVisitor btVisitor = new BroadComBlueToothVisitor();

mClient.SwitchOnBlueTooth(设备,btVisitor);
}

[TestMethod]
public void iPhoneOverIntel()
{
IMobileDevice device = new iPhone();
IBlueToothVisitor btVisitor = new IntelBlueToothVisitor();

mClient.SwitchOnBlueTooth(设备,btVisitor);
}
}

这是类的层次结构

  ///< summary> 
/// Visitable class interface
///< / summary>
接口IMobileDevice
{
///< summary>
///这是可访问类的'Accept'方法
///< / summary>
///< param name =blueToothVisitor>访问者访问类< / param>
void TurnOn(IBlueToothVisitor blueToothVisitor);
}

class iPhone:IMobileDevice
{
public void TurnOn(IBlueToothVisitor blueToothVisitor)
{
blueToothVisitor.SwitchOn(this);
}
}

class Android:IMobileDevice
{
public void TurnOn(IBlueToothVisitor blueToothVisitor)
{
blueToothVisitor.SwitchOn (这个);
}
}

class WindowsMo​​bile:IMobileDevice
{
public void TurnOn(IBlueToothVisitor blueToothVisitor)
{
blueToothVisitor.SwitchOn (这个);
}
}

接口IBlueToothRadio
{

}

class BroadComBlueToothRadio:IBlueToothRadio
{

}

class IntelBlueToothRadio:IBlueToothRadio
{

}

访客遵循 -

  ///< summary> 
/// Wiki页面 - 访问者模式将整个层次结构的逻辑运算编码为包含每种类型的一种方法的单个类。
///< / summary>
接口IBlueToothVisitor
{
void SwitchOn(iPhone设备);
void SwitchOn(WindowsMo​​bile设备);
void SwitchOn(Android设备);
}


class IntelBlueToothVisitor:IBlueToothVisitor
{
IBlueToothRadio intelRadio = new IntelBlueToothRadio();

public void SwitchOn(iPhone设备)
{
Console.WriteLine(在iPhone上开启英特尔广播);
}

public void SwitchOn(WindowsMo​​bile设备)
{
Console.WriteLine(在Windows Mobile上开启英特尔广播);
}

public void SwitchOn(Android设备)
{
Console.WriteLine(在Android上开启英特尔广播);
}
}

class BroadComBlueToothVisitor:IBlueToothVisitor
{
IBlueToothRadio broadCom = new BroadComBlueToothRadio();

public void SwitchOn(iPhone设备)
{
Console.WriteLine(在iPhone上播放Broadcom收音机);
}

public void SwitchOn(WindowsMo​​bile设备)
{
Console.WriteLine(在Windows Mobile上开启BroadCom广播);
}

public void SwitchOn(Android设备)
{
Console.WriteLine(在Android上播放Broadcom收音机);
}
}

让我走过这个结构的几点 - / p>


  1. 我有2个蓝牙访问者,其中包含在每种类型的移动设备上切换蓝牙的算法

  2. <我已经保留了蓝牙电视& BluetoothRadio分开,以遵守访客哲学 - 添加操作而不修改类本身。可能其他人希望将其合并到BluetoothRadio类本身。
  3. 每个访问者都有3个功能定义 - 每个类型的移动设备一个。

  4. 此外,由于存在6种算法变体(取决于对象的类型)双重调度是必需的。

  5. 正如我上面写的,我原来的要求是有一个这样的功能 - void SwitchOnBlueTooth(IMobileDevice mobileDevice,IBlueToothRadio blueToothRadio),现在双重调度工作我已经更改了签名 - 而不是 IBlueToothRadio 我使用 IBlueToothVisitor

请让我知道,如果有什么不清楚我们可以进一步讨论。



PS:我回答了一个问题,有些在同一行,但它有一个不同的范围&因此,为了清楚起见,我已经从我之前的答案中删除了相关部分。






编辑1



根据评论,我正在删除包装器 IBlueToothVisitor



这是没有这个包装器的访问者模式看起来像 -



但它仍然是访问者模式


  1. IMobileDevice 是Visitable类接口。


  2. IMobileDevice.TurnOn 是可访问类的接受方法。但现在它接受 IBlueToothRadio 作为访问者,而不是 IBlueToothVisitor


  3. IBlueToothRadio 成为新的访客类界面。


从而客户端中的函数签名现在更改为使用
IBlueToothRadio

  public void SwitchOnBlueTooth(IMobileDevice mobileDevice,IBlueToothRadio blueToothRadio)


Ok before marking this as a duplicate let me clarify myself. I'm reading about the visitor pattern and its applicable uses.

I've stumbled upon this post: When should I use the Visitor Design Pattern?

and the user who wrote the first answer says as follow :

Now we want to add a new operation to the hierarchy, namely we want each animal to make its sound. As far as the hierarchy is this simple, you can do it with straight polymorphism:
...
But proceeding in this way, each time you want to add an operation you must modify the interface to every single class of the hierarchy.

Now, I mostly see why it's needed from his perspective, it's basically a way to cut compilation time by making it so not every time you want to add a new polymorphic method to a class hierarchy, the whole hierarchy will get recompiled.

But he's also saying that it's fine adding a new polymorphic method to the hierarchy as long as it's a "simple" hierarchy. But my question is when do you set your line and decide on what is simple and what is not.
Also, what if an hierarchy is a complex one, but adding a new method just make total sense to be and instance method rather than having the operation in a complete different class ?

Searching a bit more I found this article explaining the visitor pattern and it's uses http://butunclebob.com/ArticleS.UncleBob.IuseVisitor

The author gave an example where writing an instance method makes the object coupled to something and moving the method to a different class (The visitor) breaks the decoupling. This made more sense to me, but I'm still not quite sure when this pattern should really be used, the first argument of "changing the hierarchy time every time you want to add a new polymorphic method..." seems to me like an excuse because if a method seems to fit in the hierarchy it should be there logically, Assuming the Animal example was a really complex hierarchy and I would have decided to add make sound method, adding an instance method would be a logical choice (in my mind).
But maybe I'm wrong so I'm here asking for more insight about this, maybe someone could enlighten me.

解决方案

Visitor pattern is suitable for incases where we need double dispatch, and the programming language does not support it.

I'm a C# developer & C# does not support it directly, And I think neither does C++. (although in newer version of C#, post C# 4.0, there is dynamic key word which may do the trick).

I'll take an example to show a situation where we need double dispatch & how visitor helps us in doing so. (Since I'm a C# developer, my code base is in C#, please bear with me, but I promise, I have tried to keep it as language neutral as possible)

Example :

Lets say I have 3 types of mobile devices - iPhone, Android, Windows Mobile.

All these three devices have a Bluetooth radio installed in them.

Lets assume that the blue tooth radio can be from 2 separate OEMs – Intel & Broadcom.

Just to make the example relevant for our discussion, lets also assume that the APIs exposes by Intel radio are different from the ones exposed by Broadcom radio.

This is how my classes look –

Now, I would like to introduce an operation – Switching On the Bluetooth on mobile device.

Its function signature should like something like this –

 void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

So depending upon Right type of device and Depending upon right type of Bluetooth radio, it can be switched on by calling appropriate steps or algorithm.

In principal, it becomes a 3 x 2 matrix, where-in I’m trying to vector the right operation depending upon the right type of objects involved.

A polymorphic behaviour depending upon the type of both the arguments.

Now, I’ll introduce Visitor pattern to this problem. Inspiration comes from the Wikipedia page stating – "In essence, the visitor allows one to add new virtual functions to a family of classes without modifying the classes themselves; instead, one creates a visitor class that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch."

Double dispatch is a necessity here due to the 3x2 matrix

Introducing Visitor pattern in code -

I have to make a decision first, which class hierarchy is more stable (less susceptible to change) – Device class hierarchy or the blue tooth class hierarchy. The one more stable will become the visitable classes & the less stable one will become visitor class. For this example, I’ll say the device class is more stable.

Here is the set-up

Here is client code & test code

 class Client
  {
      public void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothVisitor blueToothRadio) 
      {
          mobileDevice.TurnOn(blueToothRadio);        
      }
  }


 [TestClass]
public class VisitorPattern
{

    Client mClient = new Client();

    [TestMethod]
    public void AndroidOverBroadCom()
    {
        IMobileDevice device = new Android();
        IBlueToothVisitor btVisitor = new BroadComBlueToothVisitor();

        mClient.SwitchOnBlueTooth(device, btVisitor);
    }

    [TestMethod]
    public void AndroidOverIntel()
    {
        IMobileDevice device = new Android();
        IBlueToothVisitor btVisitor = new IntelBlueToothVisitor();

        mClient.SwitchOnBlueTooth(device, btVisitor);
    }

    [TestMethod]
    public void iPhoneOverBroadCom()
    {
        IMobileDevice device = new iPhone();
        IBlueToothVisitor btVisitor = new BroadComBlueToothVisitor();

        mClient.SwitchOnBlueTooth(device, btVisitor);
    }

    [TestMethod]
    public void iPhoneOverIntel()
    {
        IMobileDevice device = new iPhone();
        IBlueToothVisitor btVisitor = new IntelBlueToothVisitor();

        mClient.SwitchOnBlueTooth(device, btVisitor);
    }
}

Here are the hierarchy of classes

     /// <summary>
        /// Visitable class interface 
        /// </summary>
       interface IMobileDevice
        {
           /// <summary>
           /// It is the 'Accept' method of visitable class
           /// </summary>
            /// <param name="blueToothVisitor">Visitor Visiting the class</param>
           void TurnOn(IBlueToothVisitor blueToothVisitor);
        }

       class iPhone : IMobileDevice
       {
           public void TurnOn(IBlueToothVisitor blueToothVisitor)
           {
               blueToothVisitor.SwitchOn(this);
           }
       }

       class Android : IMobileDevice
       {
           public void TurnOn(IBlueToothVisitor blueToothVisitor)
           {
               blueToothVisitor.SwitchOn(this);
           }
       }

       class WindowsMobile : IMobileDevice
       {
           public void TurnOn(IBlueToothVisitor blueToothVisitor)
           {
               blueToothVisitor.SwitchOn(this);
           }
       }

        interface IBlueToothRadio
        {

        }

        class BroadComBlueToothRadio : IBlueToothRadio
        {

        }

        class IntelBlueToothRadio : IBlueToothRadio
        {

        }

The visitors follow -

/// <summary>
/// Wiki Page - The Visitor pattern encodes a logical operation on the whole hierarchy into a single class containing one method per type. 
/// </summary>
interface IBlueToothVisitor
{
    void SwitchOn(iPhone device);
    void SwitchOn(WindowsMobile device);
    void SwitchOn(Android device);
}


class IntelBlueToothVisitor : IBlueToothVisitor
{
    IBlueToothRadio intelRadio = new IntelBlueToothRadio();

    public void SwitchOn(iPhone device)
    {
        Console.WriteLine("Swithing On intel radio on iPhone");
    }

    public void SwitchOn(WindowsMobile device)
    {
        Console.WriteLine("Swithing On intel radio on Windows Mobile");
    }

    public void SwitchOn(Android device)
    {
        Console.WriteLine("Swithing On intel radio on Android");
    }
}

class BroadComBlueToothVisitor : IBlueToothVisitor
{
    IBlueToothRadio broadCom = new BroadComBlueToothRadio();

    public void SwitchOn(iPhone device)
    {
        Console.WriteLine("Swithing On BroadCom radio on iPhone");
    }

    public void SwitchOn(WindowsMobile device)
    {
        Console.WriteLine("Swithing On BroadCom radio on Windows Mobile");
    }

    public void SwitchOn(Android device)
    {
        Console.WriteLine("Swithing On BroadCom radio on Android");
    }
}

Let me walk through some points of this structure –

  1. I have 2 Bluetooth visitors, which contain the algorithm to switch on Bluetooth on each type of mobile device
  2. I have kept BluetoothVistor & BluetoothRadio separate so as to stick to the visitor philosophy – "Add operations without modifying the classes themselves ". May be others would like to merge it into BluetoothRadio class itself.
  3. Each visitor has 3 functions defined – one for each type mobile device.
  4. Also here since 6 variants of algorithm exist (depending upon the type of object) double dispatch is a necessity.
  5. As I wrote above my original requirement was to have a function like this - void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio), now for double dispatch to work I have changed the signature – instead of IBlueToothRadio I use IBlueToothVisitor

Please let me know if any bit is unclear we can discuss it further.

PS: I answered a question, somewhat on the same lines but it had a different scope & references, therefor I have taken out relevant parts from my previous answer here for clarity.


Edit 1

As per the comments, I'm removing the wrapper IBlueToothVisitor

This is how the visitor pattern will look like without this wrapper -

However its still is a visitor pattern

  1. IMobileDevice is the Visitable class interface.

  2. IMobileDevice.TurnOn is the 'Accept' method of visitable class. But now it accepts IBlueToothRadio as the visitor instead of IBlueToothVisitor

  3. IBlueToothRadio becomes the new visitor class interface.

Thereby the The function signature in client is now changed to use IBlueToothRadio

  public void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

这篇关于你什么时候应该真正使用访客模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆