访问者 vs 仆人 vs 命令模式

visitor vs servant vs command patterns

Here 讨论了命令模式和仆人模式的相似之处。但另一方面,我看到 Servant 与 Visitor 非常相似,而且非常相似,以至于我根本不知道有什么区别?两者都通过添加功能为其他 class 对象服务。但是命令模式不添加功能,而是包装它,对吗?请解释我的困惑在哪里。

我将尝试描述我对此事的看法和理解,也许我们可以就此进一步讨论。

命令: 如您所写 - 它包装了一个功能。 除了功能之外,它还保存要操作的数据和应用方法时要传递的参数。

命令的 Execute 方法知道如何将所有部分组合在一起以完成工作。

因此,我将命令视为一个自治的工作容器。可以稍后存储和执行。

Servant : 这是一个简单的模式,专注于通过承担责任来减轻 Master class(或客户 class)的责任在仆人或帮手中出现 class。

命令与仆人的区别

时间分离 - 命令是一个自治容器,可以存储/排队/排序或计划,并且可以在稍后的时间点执行。
此外,命令模式遵循更黑盒的编程模型,因为命令的调用者只需要调用“Execute”函数。

因此一个命令可以由一个 class 创建并由另一个调用。

访客模式及其区别

我举个例子来说明区别 -

假设我有 3 种类型的移动设备 - iPhone、Android、Windows 移动设备。

这三台设备都安装了蓝牙无线电。

让我们假设蓝牙无线电可以来自 2 个独立的 OEM – Intel 和 Broadcom。

为了使示例与我们的讨论相关,我们还假设 Intel radio 公开的 API 与 Broadcom radio 公开的 API 不同。

这就是我的 class 的样子 –

下面介绍一个操作——移动设备的蓝牙开启。

它的函数签名应该是这样的 –

 void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

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

原则上,它变成了一个 3 x 2 矩阵,其中我试图根据所涉及对象的正确类型来向量化正确的操作。

取决于两个参数类型的多态行为。

正如 wiki 页面在“动机”部分所说,解决此类问题的天真方法会遇到很多问题。

现在,我将介绍访问者模式来解决这个问题。灵感来自 Wikipedia 页面说明 – “本质上,访问者允许向 classes 系列添加新的虚拟功能,而无需修改 classes 本身;相反,创建一个访问者 class 来实现虚函数的所有适当的特化。访问者以实例引用为输入,通过双重调度实现目标。”

由于 3x2 矩阵,这里必须进行双重分派

在代码中引入访客模式 -

我必须首先做出决定,class 层次结构更稳定(不易发生变化)– 设备 class 层次结构或蓝牙 class 层次结构。 更稳定的将成为可访问的 classes,而不太稳定的将成为访问者 class。对于这个例子,我会说设备 class 更稳定。

这是设置

这是客户端代码和测试代码

 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);
    }
}

这是 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
        {

        }

访客关注 -

/// <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");
    }
}

在进入仆人模式之前,让我先了解一下这种结构的一些要点–

  1. 我有2个蓝牙访客,其中包含在每种类型的移动设备上打开蓝牙的算法
  2. 我将 BluetoothVistor 和 BluetoothRadio 分开,以坚持访问者的理念——“添加操作而不修改 classes 本身”。也许其他人想将它合并到 BluetoothRadio class 本身。
  3. 每位访问者都定义了 3 个功能 - 一种用于每种类型的移动设备。 (这是与 Servant 模式的一大区别 – Servant 模式应该只为所有服务的 classes 提供单一算法。)
  4. 此外,由于存在 6 种算法变体(取决于对象的类型)双重分派是必要的。在仆人模式中,我们只讨论算法的一个变体。
  5. 正如我在上面所写的那样,我最初的要求是具有这样的功能 - void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio),现在为了双重分派工作我已经更改了签名 - 而不是 IBlueToothRadio 我使用 IBlueToothVisitor

现在让我们看看同一个案例,让我们来实现 Servant 模式。

仆人模式是一个更简单的模式,它只是旨在从 classes 的层次结构中取出通用功能,这样它就不会在所有这些模式中重复。

为此,我们假设所有 3 台设备都需要完全相同的算法来打开蓝牙。 此外,我们假设只存在一种类型的 Radio。

现在我们可以在所有 3 个设备中编写相同的算法 classes 或者我们可以应用 wiki 所说的仆人模式 – “仆人用于为class 组。与其在每个 class 中定义该行为——或者当我们无法在公共父 class 中分解出该行为时——它在 Servant 中定义一次。”

我已经指出了与红圈的区别

  1. 不需要双重调度,客户端直接调用 被服务者的仆人 class
  2. 适用于所有 3 台设备的单一算法

这是客户端(这是唯一处理调度的地方)和测试代码

class Client
 {
    public void SwitchOnBlueTooth(IMobileDevice mobileDevice,    IBlueToothServant blueToothRadio)
    {
        //there is just one BT servant & all the serviced types get the same service (No There is no specificity). 
        // Wiki page - User knows the servant (in which case he doesn’t need to know the serviced classes) and sends messages with his requests to the servant instances, passing the serviced objects as parameters.
        blueToothRadio.SwitchOn(mobileDevice);
    }
}


[TestClass]
public class ServantPattern
{

    Client mClient = new Client();

    [TestMethod]
    public void AndroidBlueToothOn()
    {
        IMobileDevice device = new Android();
        IBlueToothServant btServant = new BlueToothServant();

        mClient.SwitchOnBlueTooth(device, btServant);
    }

    [TestMethod]
    public void iPhoneOverBroadCom()
    {
        IMobileDevice device = new iPhone();
        IBlueToothServant btServant = new BlueToothServant();

        mClient.SwitchOnBlueTooth(device, btServant);
    }

    [TestMethod]
    public void WMBlueToothOn()
    {
        IMobileDevice device = new WindowsMobile();
        IBlueToothServant btServant = new BlueToothServant();

        mClient.SwitchOnBlueTooth(device, btServant);
    }
}

服务的 class 层次结构在这里并不那么有趣

/// <summary>
/// Serviced class interface 
/// </summary>
interface IMobileDevice
{

}

class iPhone : IMobileDevice
{

}

class Android : IMobileDevice
{
}

class WindowsMobile : IMobileDevice
{
}

这是仆人 class 及其界面(wiki link 没有显示它的界面)

 /// <summary>
 /// The sevant interface
 /// </summary>
 /// <remarks>Not present in Wiki article but I have added so its easy to          mock it</remarks>
 interface IBlueToothServant
 {
     void SwitchOn(IMobileDevice device);
 }


class BlueToothServant : IBlueToothServant
{
    IBlueToothRadio intelRadio = new BlueToothRadio();

    public void SwitchOn(IMobileDevice device)
    {
        Console.WriteLine("Switching On blue tooth radio on IMobileDevice");
    }

}

我没有粘贴 IBlueToothRadioBlueToothRadio 的代码,因为这与讨论仆人模式不太相关。

如果有任何不清楚的地方,请告诉我,我们可以进一步讨论。