Ответ 1
Шаблон посетителя подходит для случаев, когда нам нужна двойная отправка, а язык программирования не поддерживает его.
Я разработчик С#, а С# не поддерживает его напрямую. И я думаю, что ни С++. (хотя в более новой версии С#, post С# 4.0, есть ключевое слово dynamic
, которое может сделать трюк).
Я приведу пример, чтобы показать ситуацию, когда нам нужна двойная отправка и как посетитель помогает нам в этом. (Поскольку я разработчик С#, моя база кода находится на С#, пожалуйста, несите меня, но я обещаю, что я старался сохранить ее как можно более нейтральной)
Пример:
Предположим, у меня есть 3 типа мобильных устройств - iPhone, Android, Windows Mobile.
Все эти три устройства имеют Bluetooth-радио, установленное в них.
Предположим, что радиостанция с синим зубом может быть от двух отдельных OEM-производителей - Intel и Broadcom.
Чтобы сделать пример подходящим для нашего обсуждения, давайте также предположим, что API-интерфейсы, предоставляемые радиостанцией Intel, отличаются от тех, которые выставлены радио Broadcom.
Вот как выглядят мои классы -
Теперь я хотел бы ввести операцию "Включение Bluetooth на мобильном устройстве".
Его сигнатура функции должна выглядеть примерно так:
void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)
Итак, в зависимости от правильного типа устройства и В зависимости от правильного типа радиосвязи Bluetooth его можно включить с помощью вызова соответствующих шагов или алгоритма.
В принципе, он становится матрицей 3 x 2, где-в Im пытается вектор правильной операции в зависимости от правильного типа объектов.
Полиморфное поведение в зависимости от типа обоих аргументов.
Теперь, я буду вводить шаблон посетителя для этой проблемы. Вдохновение происходит со страницы Википедии: "По сути, посетитель позволяет добавлять новые виртуальные функции в семейство классов без изменения самих классов, вместо этого создается класс посетителей, который реализует все соответствующие специализации виртуальной функции Посетитель берет ссылку на экземпляр в качестве входных данных и реализует цель посредством двойной отправки".
Двойная отправка необходима из-за матрицы 3x2
Представление шаблона посетителя в коде -
Сначала мне нужно принять решение, какая иерархия классов более стабильна (менее подвержена изменениям) - иерархия классов устройств или иерархия классов синего зуба. Еще одной стабильностью станут посещаемые классы, а менее стабильная станет классом посетителей. В этом примере Ill говорит, что класс устройства более стабилен.
Вот настройка
Вот код клиента и тестовый код
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 функции, определенные - по одному для каждого мобильного устройства типа.
- Также здесь, поскольку существует 6 вариантов алгоритма (в зависимости от типа объекта) требуется двойная отправка.
- Как я писал выше, мое первоначальное требование состояло в том, чтобы иметь такую функцию -
void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)
, теперь для двойной отправки на работу я изменил подпись - вместоIBlueToothRadio
используюIBlueToothVisitor
Пожалуйста, дайте мне знать, если какой-либо бит неясен, мы можем обсудить его дальше.
PS: Я ответил на вопрос, несколько на тех же строках, но у него были разные возможности и ссылки, поэтому для ясности я извлек соответствующие части из моего предыдущего ответа.
Изменить 1
В соответствии с комментариями я удаляю оболочку IBlueToothVisitor
Вот как будет выглядеть шаблон посетителя без этой обертки -
Однако он все еще является шаблоном посетителя
-
IMobileDevice
- это интерфейс класса Visitable. -
IMobileDevice.TurnOn
- это метод "Accept" для посещаемого класса. Но теперь он принимаетIBlueToothRadio
в качестве посетителя вместоIBlueToothVisitor
-
IBlueToothRadio
становится новым интерфейсом класса посетителей.
Таким образом, подпись функции в клиенте теперь изменена для использования IBlueToothRadio
public void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)
public void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)