шаблоны посетитель против слуги против команды

Здесь обсуждается сходство шаблонов Command и Servant. Но с другой стороны я вижу, что Servant очень похож на Visitor и настолько похож, что я вообще не знаю, в чем разница? Оба служат для других объектов класса, добавляя функциональность. Но шаблон команды не добавляет функциональности, а оборачивает ее, верно? Объясните, пожалуйста, в чем мое замешательство.


person Narek    schedule 13.08.2015    source источник
comment
Для посетителя и слуги обратитесь к этому вопросу: stackoverflow.com/questions/27939046/   -  person Ravindra babu    schedule 13.08.2015
comment
Шаблон посетителя избегает иерархии и позволяет нам изменять функциональность без изменения контракта. Паттерн команды основан на реализации контракта.   -  person Ravindra babu    schedule 13.08.2015


Ответы (1)


Я попытаюсь описать свое мнение и понимание по этому вопросу, и, возможно, мы сможем обсудить его дальше.

Команда: Как вы написали, она оборачивает функциональность. Наряду с функциональностью он также содержит данные, с которыми нужно работать, и аргументы, которые необходимо передать при применении метода.

Метод Execute команды знает, как собрать все части воедино, чтобы выполнить работу.

Поэтому я смотрю на команду как на автономный контейнер работы. Которые можно сохранить и выполнить позже.

Servant: это простой паттерн, который фокусируется на освобождении мастер-класса (или клиентского класса) от его обязанностей, перенося их (обязанности) в класс серванта или помощника.

Разница между командой и слугой

Разделение во времени. Команды, являющиеся автономным контейнером, могут быть сохранены/поставлены в очередь/упорядочены или запланированы и могут быть выполнены в более поздний момент времени. нужно вызвать только функцию 'Execute'.

Таким образом, команда может быть создана одним классом и вызвана другим.

Шаблон посетителя и его отличие

Я возьму пример, чтобы объяснить разницу -

Допустим, у меня есть 3 типа мобильных устройств — iPhone, Android, Windows Mobile.

Во всех этих трех устройствах установлено радио Bluetooth.

Предположим, что bluetooth-радио может быть от двух разных OEM-производителей — Intel и Broadcom.

Просто чтобы сделать пример актуальным для нашего обсуждения, давайте также предположим, что API-интерфейсы, предоставляемые радио Intel, отличаются от тех, которые предоставляет радио Broadcom.

Вот так выглядят мои занятия

введите здесь описание изображения

введите здесь описание изображения

Теперь я хотел бы представить операцию — включение Bluetooth на мобильном устройстве.

Сигнатура его функции должна выглядеть примерно так:

 void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

Таким образом, в зависимости от правильного типа устройства и в зависимости от правильного типа Bluetooth-радио его можно включить, вызвав соответствующие шаги или алгоритм. .

В принципе, это становится матрицей 3 x 2, в которой я пытаюсь векторизировать правильную операцию в зависимости от правильного типа задействованных объектов.

Полиморфное поведение в зависимости от типа обоих аргументов.введите здесь описание изображения

Итак, как говорится на вики-странице в разделе «Мотивация», наивный способ решения такой проблемы будет страдать от множества проблем.

Теперь я познакомлю вас с шаблоном «Посетитель» для решения этой проблемы. Вдохновение исходит от страницы Википедии, в которой говорится: «По сути, посетитель позволяет добавлять новые виртуальные функции в семейство классов, не изменяя сами классы; вместо этого создается класс посетителя, который реализует все соответствующие специализации виртуальной функции. Посетитель принимает ссылку на экземпляр в качестве входных данных и реализует цель с помощью двойной отправки».

Двойная отправка здесь необходима из-за матрицы 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 посетителя Bluetooth, которые содержат алгоритм включения Bluetooth на каждом типе мобильного устройства.
  2. Я разделил BluetoothVistor и BluetoothRadio, чтобы придерживаться философии посетителя: «Добавляйте операции, не изменяя сами классы». Возможно, другие захотят объединить его с самим классом BluetoothRadio.
  3. Для каждого посетителя определены 3 функции — по одной для каждого типа мобильного устройства. (это одно большое отличие от шаблона Servant — предполагается, что шаблон Servant предлагает только один алгоритм для всех обслуживаемых классов.)
  4. Также здесь, поскольку существует 6 вариантов алгоритма (в зависимости от типа объекта), двойная отправка является необходимостью. В шаблоне серванта мы говорим только об одном варианте алгоритма.
  5. Как я уже писал выше, моим первоначальным требованием было иметь такую ​​функцию - void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio), теперь для работы двойной отправки я изменил подпись - вместо IBlueToothRadio использую IBlueToothVisitor

Теперь давайте рассмотрим тот же случай и реализуем шаблон Servant.

Шаблон Servant — это гораздо более простой шаблон, он просто направлен на удаление общей функциональности из иерархии классов, чтобы она не дублировалась во всех них.

Для этого предположим, что всем трем устройствам нужен один и тот же алгоритм для включения Bluetooth. Кроме того, мы предполагаем, что существует только один тип радио.

Теперь мы можем либо написать один и тот же алгоритм для всех трех классов устройств, либо применить шаблон серванта, как говорит вики — «Сервант используется для обеспечения некоторого поведения для группы классов. Вместо того, чтобы определять это поведение в каждом классе — или когда мы не можем выделить это поведение в общем родительском классе — оно определяется один раз в 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
{
}

вот класс слуги и его интерфейс (ссылка на вики не показывает для него интерфейс)

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

}

Я не вставлял код для IBlueToothRadio и BlueToothRadio, поскольку он не слишком актуален для обсуждения шаблона слуги.

Пожалуйста, дайте мне знать, если что-то неясно, мы можем обсудить это дальше.

person Kapoor    schedule 20.09.2015
comment
Вы имеете в виду, что Посетитель запрограммирован и интерфейс, а Слуга - в реализации? Если да, то не вижу смысла говорить о Servant как о шаблоне. - person Narek; 22.09.2015
comment
Привет @Narek, нет, я не это имею в виду. Пусть шаблон слуги запрограммирован на интерфейс. На самом деле программирование интерфейса будет полезно для имитации класса-слуги, если мы хотим иметь автоматизированные модульные тесты. - person Kapoor; 22.09.2015
comment
Я хочу сказать, что в случае шаблона слуги существует только одна реализация класса слуги, которая предлагает один и тот же сервис для всех обслуживаемых классов. В поведении служб, предоставляемых обслуживаемым классам, нет никакой специфики. Поэтому это подходит, когда всем обслуживаемым классам требуется единое поведение службы. - person Kapoor; 22.09.2015
comment
Однако, если разным обслуживаемым классам требуется разное поведение службы, я выберу двойную диспетчеризацию, используя шаблон посетителя. Шаблон посетителя подходит для условий, когда каждому обслуживаемому классу требуется определенное поведение службы. Это поведение реализуется отдельным посетителем. - person Kapoor; 22.09.2015
comment
Все еще не понимаю вашей точки зрения. Не могли бы вы написать пример кода и сравнить его на примере Visitor и Servant. На данный момент мне кажется, что Слуга нам вообще не нужен. (Кстати, когда вы говорите о двойной отправке, вы имеете в виду динамическую отправку или так называемый полиморфизм, верно?) - person Narek; 23.09.2015
comment
Конечно, @Narek, было бы неплохо написать короткий код. Я также создам диаграмму UML для кода. Но на данный момент меня нет, и я вернусь на свою рабочую станцию ​​26 или 27 числа. - person Kapoor; 23.09.2015
comment
А пока, О двойной отправке… Да, под двойной отправкой я подразумеваю динамическую отправку. Это полиморфное поведение присутствует в шаблоне посетителя, потому что существуют разные реализации одного и того же контракта (в виде разных конкретных классов посетителей). В шаблоне серванта это полиморфное поведение отсутствует, потому что существует только одна реализация серванта. В любом случае, я попытаюсь передать свою точку зрения с помощью кода и диаграммы. а сейчас до свидания - person Kapoor; 23.09.2015
comment
Здравствуйте @Narek, я сожалею о задержке .. Это был мой день рождения, и у меня были продолжительные каникулы. Я привел пример. В моем примере я сначала объяснил, как я смотрю на шаблон посетителя, возможно, я пошел немного длиннее, но это помогло мне объяснить отличие от шаблона слуги. Дайте мне знать, если вы поняли мою точку зрения ... мы можем обсудить это дальше, и теперь с примером мы можем иметь точку отсчета в наших обсуждениях. - person Kapoor; 04.10.2015
comment
Капур, я думаю, что это лучший ответ, который только может быть! И, кстати, с днем ​​рождения, с некоторой задержкой! :) - person Narek; 07.10.2015