Здесь обсуждается сходство шаблонов Command и Servant. Но с другой стороны я вижу, что Servant очень похож на Visitor и настолько похож, что я вообще не знаю, в чем разница? Оба служат для других объектов класса, добавляя функциональность. Но шаблон команды не добавляет функциональности, а оборачивает ее, верно? Объясните, пожалуйста, в чем мое замешательство.
шаблоны посетитель против слуги против команды
Ответы (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");
}
}
Позвольте мне пройтись по некоторым пунктам этой структуры, прежде чем перейти к шаблону слуги.
- У меня есть 2 посетителя Bluetooth, которые содержат алгоритм включения Bluetooth на каждом типе мобильного устройства.
- Я разделил BluetoothVistor и BluetoothRadio, чтобы придерживаться философии посетителя: «Добавляйте операции, не изменяя сами классы». Возможно, другие захотят объединить его с самим классом BluetoothRadio.
- Для каждого посетителя определены 3 функции — по одной для каждого типа мобильного устройства. (это одно большое отличие от шаблона Servant — предполагается, что шаблон Servant предлагает только один алгоритм для всех обслуживаемых классов.)
- Также здесь, поскольку существует 6 вариантов алгоритма (в зависимости от типа объекта), двойная отправка является необходимостью. В шаблоне серванта мы говорим только об одном варианте алгоритма.
- Как я уже писал выше, моим первоначальным требованием было иметь такую функцию -
void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)
, теперь для работы двойной отправки я изменил подпись - вместоIBlueToothRadio
используюIBlueToothVisitor
Теперь давайте рассмотрим тот же случай и реализуем шаблон Servant.
Шаблон Servant — это гораздо более простой шаблон, он просто направлен на удаление общей функциональности из иерархии классов, чтобы она не дублировалась во всех них.
Для этого предположим, что всем трем устройствам нужен один и тот же алгоритм для включения Bluetooth. Кроме того, мы предполагаем, что существует только один тип радио.
Теперь мы можем либо написать один и тот же алгоритм для всех трех классов устройств, либо применить шаблон серванта, как говорит вики — «Сервант используется для обеспечения некоторого поведения для группы классов. Вместо того, чтобы определять это поведение в каждом классе — или когда мы не можем выделить это поведение в общем родительском классе — оно определяется один раз в Servant».
Я указал на разницу с красными кругами
- Нет необходимости в двойной отправке, клиент может напрямую вызывать сервант для обслуживаемого класса.
- Единый алгоритм для всех 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
, поскольку он не слишком актуален для обсуждения шаблона слуги.
Пожалуйста, дайте мне знать, если что-то неясно, мы можем обсудить это дальше.