Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

visitor vs servant vs command patterns

Here the resemblance of Command and Servant patterns is discussed. But on the other hand I see that Servant is very similar to Visitor and so much similar that I don't know what is the difference at all? Both serve for other class objects by adding functionality. But command pattern does not add functionality, instead wraps it, right? Please explain where is my confusion.

like image 671
Narek Avatar asked Dec 10 '22 21:12

Narek


1 Answers

I'll try to describe my opinion & understanding over the matter and perhaps we can discuss further over it.

Command : As you wrote - It wraps a functionality. Along with the functionality, it also holds the data to be operated upon and the arguments to be passed when the method is applied.

The Execute method of the command knows how to bring all the pieces together to get the work done.

Therefore, I look at command as an autonomous container of work. Which can be stored & executed later.

Servant : Its a simple pattern which focuses on relieving the Master class (or client class) from its responsibilities by taking them (responsibilities) out in a servant or helper class.

Difference between Command & servant

Time Separation - Commands being an autonomous container, can be stored / queued / sequenced or scheduled and can be executed at a later point of time.
Also, command pattern follows a more black box programming model, since the invoker of the command needs to call only 'Execute' function.

So a command could be created by one class & invoked by another.

Visitor Pattern and its difference

I'll take an example to explain the difference -

Let's say I have 3 types of mobile devices - iPhone, Android, Windows Mobile.

All these three devices have a Bluetooth radio installed in them.

Let's assume that the blue tooth radio can be from 2 separate OEMs – Intel & Broadcom.

Just to make the example relevant for our discussion, let's also assume that the APIs exposes by Intel radio are different from the ones exposed by Broadcom radio.

This is how my classes look –

enter image description here

enter image description here

Now, I would like to introduce an operation – Switching On the Bluetooth on a mobile device.

Its function signature should like something like this –

 void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio)

So depending upon the Right type of device and Depending upon the right type of Bluetooth radio, it can be switched on by calling appropriate steps or algorithm.

In principle, it becomes a 3 x 2 matrix, wherein I’m trying to vector the right operation depending upon the right type of objects involved.

A polymorphic behaviour depending upon the type of both the arguments.enter image description here

So as wiki page says in ‘Motivation’ section A naive way to solve such a problem will suffer from a lot of problems.

Now, I’ll introduce Visitor pattern to this problem. Inspiration comes from the Wikipedia page stating – “In essence, the visitor allows one to add new virtual functions to a family of classes without modifying the classes themselves; instead, one creates a visitor class that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.”

Double dispatch is a necessity here due to the 3x2 matrix

Introducing Visitor pattern in code -

I have to make a decision first, which class hierarchy is more stable (less susceptible to change) – Device class hierarchy or the blue tooth class hierarchy. The one more stable will become the visitable classes & the less stable one will become visitor class. For this example, I’ll say the device class is more stable.

Here is the set-up

enter image description here

Here is client code & test code

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

Here is the hierarchy of 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
        {

        }

The visitors follow -

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

Let me walk through some points of this structure before going to servant pattern–

  1. I have 2 Bluetooth visitors, which contain the algorithm to switch on Bluetooth on each type of mobile device
  2. I have kept BluetoothVistor & BluetoothRadio separate so as to stick to the visitor philosophy – “Add operations without modifying the classes themselves ”. Maybe others would like to merge it into BluetoothRadio class itself.
  3. Each visitor has 3 functions defined – one for each type of mobile device. (this is one big difference from Servant pattern – Servant pattern is supposed to offer only a single algorithm to all the serviced classes.)
  4. Also here since 6 variants of algorithm exist (depending upon the type of object) double dispatch is a necessity. In a servant pattern we are talking about only a single variant of the algorithm.
  5. As I wrote above my original requirement was to have a function like this - void SwitchOnBlueTooth(IMobileDevice mobileDevice, IBlueToothRadio blueToothRadio), now for double dispatch to work I have changed the signature – instead of IBlueToothRadio I use IBlueToothVisitor

Now let's look at the same case & let's implement Servant pattern.

Servant pattern is a much simpler pattern, it just aims to take out common functionality from a hierarchy of classes so that it's not duplicated in all of them.

For this let's assume, that all the 3 devices need exactly the same algorithm to switch on Bluetooth. Also, we assume that there is only one type of Radio that exists.

Now either we can write the same algorithm in all the 3 device classes or we can apply the servant pattern as wiki says – “Servant is used for providing some behaviour to a group of classes. Instead of defining that behaviour in each class - or when we cannot factor out this behaviour in the common parent class - it is defined once in the Servant.”

enter image description here

I have pointed out the difference with Red Circles

  1. There is no double dispatch needed, the client can directly call the servant for the serviced class
  2. A single algorithm for all the 3 devices

Here is the client (which is the only place where dispatch is handled) & test code

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

The serviced class hierarchy is not that interesting here

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

}

class iPhone : IMobileDevice
{

}

class Android : IMobileDevice
{
}

class WindowsMobile : IMobileDevice
{
}

here is the servant class & its interface (wiki link doesn't show an interface for it)

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

}

I have not pasted the code for IBlueToothRadio & BlueToothRadio since that is not too relevant for discussing servant pattern.

Please let me know if any bit is unclear we can discuss it further.

like image 97
Kapoor Avatar answered Dec 28 '22 08:12

Kapoor