【问题标题】:visitor vs servant vs command patterns访客 vs 仆人 vs 命令模式
【发布时间】:2015-11-06 07:25:01
【问题描述】:

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

【问题讨论】:

  • 访客vs仆人,请参考这个问题:stackoverflow.com/questions/27939046/…
  • 访问者模式摆脱了层次结构,允许我们在不改变合约的情况下修改功能。命令模式基于实现一个契约。

标签: design-patterns visitor-pattern command-pattern


【解决方案1】:

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

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

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

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

Servant :它是一个简单的模式,它专注于通过将 Master 类(或 client 类)从其职责中取出,将它们(职责)放到一个servant 或 helper 类中。

命令与仆人的区别

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

所以一个命令可以由一个类创建并由另一个类调用。

访客模式及其区别

我会举个例子来解释一下区别 -

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

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

让我们假设蓝牙收音机可以来自 2 个独立的 OEM——英特尔和博通。

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

这就是我的课程的样子 –

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

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

 void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

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

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

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

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

现在,我将介绍访问者模式来解决这个问题。灵感来自 Wikipedia 页面声明 – “本质上,访问者允许在不修改类本身的情况下将新的虚函数添加到类族中;相反,我们创建了一个访问者类,它实现了虚函数的所有适当的特化。访问者将实例引用作为输入,通过双重调度实现目标。”

由于 3x2 矩阵,此处需要双重分派

在代码中引入访问者模式 -

我必须首先做出决定,哪个类层次结构更稳定(不易更改)——设备类层次结构还是蓝牙类层次结构。 更稳定的将成为可访问的类,而不太稳定的将成为访客类。对于这个例子,我会说设备类更稳定。

这里是设置

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

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

这是类的层次结构

     /// <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 分开,以坚持访问者的理念——“添加操作而不修改类本身”。也许其他人想将它合并到 BluetoothRadio 类本身。
  3. 每个访问者都定义了 3 个功能 - 每种类型的移动设备一个。 (这是与仆人模式的一大区别——仆人模式应该只为所有服务类提供单一算法。)
  4. 同样在这里,因为存在 6 种算法变体(取决于对象的类型)双重分派是必要的。在仆人模式中,我们只讨论算法的一个变体。
  5. 正如我在上面写的那样,我最初的要求是拥有这样的函数 - void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio),现在为了让双重调度工作我已经更改了签名 - 而不是 IBlueToothRadio 我使用 IBlueToothVisitor

现在让我们看看同样的案例,让我们实现仆人模式。

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

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

现在我们可以在所有 3 个设备类中编写相同的算法,或者我们可以应用 wiki 所说的仆人模式——“仆人用于为一组类提供一些行为。而不是在每个类中定义该行为 - 或者当我们无法在公共父类中排除此行为时 - 它在 Servant 中定义一次。”

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

  1. 不需要双重调度,客户端可以直接调用 服务班的仆人
  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);
    }
}

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

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

}

class iPhone : IMobileDevice
{

}

class Android : IMobileDevice
{
}

class WindowsMobile : IMobileDevice
{
}

这里是服务类及其接口(wiki 链接没有显示它的接口)

 /// <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 的代码,因为这与讨论仆人模式不太相关。

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

【讨论】:

  • 你的意思是Visitor被编程并在实现中接口和Servant?如果是的话,我认为将 Servant 作为一种模式讨论没有任何意义。
  • 嗨@Narek,不,我不是这个意思。让仆人模式被编程为一个接口。如果我们希望进行自动化单元测试,对接口的实际编程将有助于模拟服务类。
  • 我的观点是,对于 servant 模式,只有 一个servant 类实现,它为所有服务类提供相同的服务。提供给服务类的服务行为没有特殊性。 因此,它适用于所有服务类都需要服务的单一行为
  • 但是,如果不同的服务类需要不同的服务行为,那么我将使用访问者模式进行双重调度。访问者模式适用于每个服务类都需要服务的特定行为的情况。该行为由单独的访问者实现。
  • 还是不明白你的意思。您能否编写一个代码示例并比较示例访客和仆人。现在,在我看来,我们根本不需要Servant。 (顺便说一句,当您说双重分派时,您的意思是动态分派或所谓的多态性,对吗?)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多