Behavioral Design Pattern

1. Strategy Pattern

The strategy pattern is a behavioral design pattern that enables selecting an algorithm at runtime

The key idea is to create objects which represent various strategies. These objects form a pool of strategies from which the context object can choose from to vary its behavior as per its strategy.

Sorting algorithms have a set of rule specific to each other they follow to effectively sort an array of numbers. We have the

·       Bubble Sort

·       Linear Search

·       Heap Sort

·       Merge Sort

·       Selection Sort

Strategy Pattern supports the SOLID principles and forces us to abide by it. The D in SOLID says we must depend on abstractions, not on concretions.


// In an OOP Language -
// TypeScript
// interface all sorting algorithms must implement
interface SortingStrategy {
    sort(array);
}
// heap sort algorithm implementing the `SortingStrategy` interface, it implements its algorithm in the `sort` method
class HeapSort implements SortingStrategy {
    sort() {
        log("HeapSort algorithm")
        // implementation here
    }
}
// linear search sorting algorithm implementing the `SortingStrategy` interface, it implements its algorithm in the `sort` method
class LinearSearch implements SortingStrategy {
    sort(array) {
        log("LinearSearch algorithm")
        // implementation here
    }
}
class SortingProgram {
    private sortingStrategy: SortingStrategy
    constructor(array: Array<Number>) {
    }
    runSort(sortingStrategy: SortingStrategy) {
        return this.sortingStrategy.sort(this.array)
    }
}
// instantiate the `SortingProgram` with an array of numbers
const sortProgram = new SortingProgram([9,2,5,3,8,4,1,8,0,3])
// sort using heap sort
sortProgram.runSort(new HeapSort())
// sort using linear search
sortProgram.runSort(new LinearSearch())

 


Read More

 2. Observer Design Pattern

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.

Problem:

Imagine that you have two types of objects: a Customer and a Store. The customer is very interested in a particular brand of product (say, it’s a new model of the iPhone) which should become available in the store very soon.

The customer could visit the store every day and check product availability. But while the product is still en route, most of these trips would be pointless.

On the other hand, the store could send tons of emails (which might be considered spam) to all customers each time a new product becomes available. This would save some customers from endless trips to the store. At the same time, it’d upset other customers who aren’t interested in new products.

It looks like we’ve got a conflict. Either the customer wastes time checking product availability or the store wastes resources notifying the wrong customers.


namespace Observer
{
	public class Program
	{
		public static void Main()
		{
			var per = new Person("Subash");
			Console.WriteLine(per.PersonName);
			var profile = new ProfileUpdate(per);
			profile.SubscribeEvent();
			profile.ChangeName("Selvan");
			Console.WriteLine(per.PersonName);
			profile.UnSubscribeEvent();

		}
	}


	public class NotifyPropertyChanged : INotifyPropertyChanged
	{
		private bool _eventsSourceEnabled;
		private List<string> _propertyNamesCollection = new List<string>();

		public event PropertyChangedEventHandler PropertyChanged;

		public void StartEventSourcing()
		{
			_eventsSourceEnabled = true;
		}

		public void StopEventSourcing(bool distinctEvents = true)
		{
			_eventsSourceEnabled = false;

			var currentBuffer = _propertyNamesCollection.ToList();
			_propertyNamesCollection.Clear();

			if (distinctEvents)
			{
				currentBuffer = currentBuffer.Distinct().ToList();
			}

			foreach (var propertyName in currentBuffer)
			{
				RaiseEvent(propertyName);
			}
		}

		protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
		{
			if (_eventsSourceEnabled)
			{
				_propertyNamesCollection.Add(propertyName);
			}
			else
			{
				RaiseEvent(propertyName);
			}
		}

		private void RaiseEvent(string propertyName)
		{
			PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
		}
	}


	public class Person : NotifyPropertyChanged
	{
		private string name;

		public Person(string Name)
		{
			name = Name;
		}

		public string PersonName
		{
			get { return name; }
			set
			{
				name = value;
				// Call OnPropertyChanged whenever the property is updated
				RaisePropertyChanged();
			}
		}
	}

	public class ProfileUpdate
	{
		private Person perObj = null;
		public ProfileUpdate(Person obj)
		{
			perObj = obj;
		}

		public void SubscribeEvent()
		{
			perObj.PropertyChanged += OnPropertyChangedEvent;
		}

		public void UnSubscribeEvent()
		{
			perObj.PropertyChanged -= OnPropertyChangedEvent;
		}

		public void ChangeName(string Name)
		{
			perObj.PersonName = Name;
		}

		private void OnPropertyChangedEvent(object sender, PropertyChangedEventArgs e)
		{
			Console.WriteLine("Name Changed Invoked");
		}

	}

}


Class Diagram Design



Read More

 3. Command Design Pattern

Command is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you pass requests as a method arguments, delay or queue a request’s execution, and support undoable operations.

Code 

In analogy,  remote control is the client.  Stereo, lights etc. are the receivers. In command pattern there is a Command object that encapsulates a request by binding together a set of actions on a specific receiver. It does so by exposing just one method execute() that causes some actions to be invoked on the receiver.


// A simple Java program to demonstrate
// implementation of Command Pattern using
// a remote control example.

// An interface for command
interface Command
{
	public void execute();
}

// Light class and its corresponding command
// classes
class Light
{
	public void on()
	{
		System.out.println("Light is on");
	}
	public void off()
	{
		System.out.println("Light is off");
	}
}
class LightOnCommand implements Command
{
	Light light;

	// The constructor is passed the light it
	// is going to control.
	public LightOnCommand(Light light)
	{
	this.light = light;
	}
	public void execute()
	{
	light.on();
	}
}
class LightOffCommand implements Command
{
	Light light;
	public LightOffCommand(Light light)
	{
		this.light = light;
	}
	public void execute()
	{
		light.off();
	}
}

// Stereo and its command classes
class Stereo
{
	public void on()
	{
		System.out.println("Stereo is on");
	}
	public void off()
	{
		System.out.println("Stereo is off");
	}
	public void setCD()
	{
		System.out.println("Stereo is set " +
						"for CD input");
	}
	public void setDVD()
	{
		System.out.println("Stereo is set"+
						" for DVD input");
	}
	public void setRadio()
	{
		System.out.println("Stereo is set" +
						" for Radio");
	}
	public void setVolume(int volume)
	{
	// code to set the volume
	System.out.println("Stereo volume set"
						+ " to " + volume);
	}
}

class StereoOnWithCDCommand implements Command
{
	Stereo stereo;
	public StereoOnWithCDCommand(Stereo stereo)
	{
		this.stereo = stereo;
	}
	public void execute()
	{
		stereo.on();
		stereo.setCD();
		stereo.setVolume(11);
	}
}

// A Simple remote control with one button
class SimpleRemoteControl
{
	Command slot; // only one button

	public SimpleRemoteControl()
	{
	}

	public void setCommand(Command command)
	{
		// set the command the remote will
		// execute
		slot = command;
	}

	public void buttonWasPressed()
	{
		slot.execute();
	}
}

// Driver class
class RemoteControlTest
{
	public static void main(String[] args)
	{
		SimpleRemoteControl remote =
				new SimpleRemoteControl();
		Light light = new Light();
		Stereo stereo = new Stereo();

		// we can change command dynamically
		remote.setCommand(new
					LightOnCommand(light));
		remote.buttonWasPressed();
		
		remote.setCommand(new
				StereoOnWithCDCommand(stereo));
		remote.buttonWasPressed();
	}
}

 Class Diagram



Advantages:
  • Makes our code extensible as we can add new commands without changing existing code.
  • Reduces coupling between the invoker and receiver of a command.
Disadvantages:
  • Increase in the number of classes for each individual command
 4. Mediator Design Pattern

Mediator is a behavioral design pattern that lets you reduce disorder dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.

This design pattern provides a mediator object and that mediator object normally handles all the communication complexities between different objects.
The Mediator object acts as the communication center for all objects. That means when an object needs to communicate to another object, then it does not call the other object directly, instead, it calls the mediator object and it is the responsibility of the mediator object to route the message to the destination object.


Real-world code in C#

This real-world code demonstrates the Mediator pattern facilitating loosely coupled communication between different Participants registering with a Chatroom. The Chatroom is the central hub through which all communication takes place. At this point only one-to-one communication is implemented in the Chatroom, but would be trivial to change to one-to-many.

Example


using System;
using System.Collections.Generic;

namespace Mediator.RealWorld
{
    /// <summary>
    /// The 'Mediator' abstract class
    /// </summary>

    public abstract class AbstractChatroom
    {
        public abstract void Register(Participant participant);
        public abstract void Send(
            string from, string to, string message);
    }

    /// <summary>
    /// The 'ConcreteMediator' class
    /// </summary>

    public class Chatroom : AbstractChatroom
    {
        private Dictionary<string, Participant> participants = new Dictionary<string, Participant>();

        public override void Register(Participant participant)
        {
            if (!participants.ContainsValue(participant))
            {
                participants[participant.Name] = participant;
            }

            participant.Chatroom = this;
        }

        public override void Send(string from, string to, string message)
        {
            Participant participant = participants[to];

            if (participant != null)
            {
                participant.Receive(from, message);
            }
        }
    }

    /// <summary>
    /// The 'AbstractColleague' class
    /// </summary>

    public class Participant
    {
        Chatroom chatroom;
        string name;

        // Constructor

        public Participant(string name)
        {
            this.name = name;
        }

        // Gets participant name

        public string Name
        {
            get { return name; }
        }

        // Gets chatroom

        public Chatroom Chatroom
        {
            set { chatroom = value; }
            get { return chatroom; }
        }

        // Sends message to given participant

        public void Send(string to, string message)
        {
            chatroom.Send(name, to, message);
        }

        // Receives message from given participant

        public virtual void Receive(
            string from, string message)
        {
            Console.WriteLine("{0} to {1}: '{2}'",
                from, Name, message);
        }
    }

    /// <summary>
    /// A 'ConcreteColleague' class
    /// </summary>

    public class Beatle : Participant
    {
        // Constructor

        public Beatle(string name)
            : base(name)
        {
        }

        public override void Receive(string from, string message)
        {
            Console.Write("To a Beatle: ");
            base.Receive(from, message);
        }
    }

    /// <summary>
    /// A 'ConcreteColleague' class
    /// </summary>

    public class NonBeatle : Participant
    {
        // Constructor
        public NonBeatle(string name)
            : base(name)
        {
        }

        public override void Receive(string from, string message)
        {
            Console.Write("To a non-Beatle: ");
            base.Receive(from, message);
        }
    }
	
	/// <summary>
    /// Mediator Design Pattern
    /// </summary>

    public class Program
    {
        public static void Main(string[] args)
        {
            // Create chatroom

            Chatroom chatroom = new Chatroom();

            // Create participants and register them

            Participant George = new Beatle("George");
            Participant Paul = new Beatle("Paul");
            Participant Ringo = new Beatle("Ringo");
            Participant John = new Beatle("John");
            Participant Yoko = new NonBeatle("Yoko");

            chatroom.Register(George);
            chatroom.Register(Paul);
            chatroom.Register(Ringo);
            chatroom.Register(John);
            chatroom.Register(Yoko);

            // Chatting participants

            Yoko.Send("John", "Hi John!");
            Paul.Send("Ringo", "All you need is love");
            Ringo.Send("George", "My sweet Lord");
            Paul.Send("John", "Can't buy me love");
            John.Send("Yoko", "My sweet love");

            // Wait for user

            Console.ReadKey();
        }
    }
}

 
Output
To a Beatle: Yoko to John: 'Hi John!'
To a Beatle: Paul to Ringo: 'All you need is love'
To a Beatle: Ringo to George: 'My sweet Lord'
To a Beatle: Paul to John: 'Can't buy me love'
To a non-Beatle: John to Yoko: 'My sweet love'

 Class diagram


 5. Memento Design Pattern

Memento is a behavioral design pattern that lets you save and restore the previous state of an object without revealing the details of its implementation.

When we need to use the Memento Design Pattern

The state of an object needs to be saved and restored at a later time.
The state of an object cannot be exposed directly by using an interface without exposing implementation.

Real-world code in C#

This real-world code demonstrates the Memento pattern which temporarily saves and then restores the SalesProspect's internal state.

Example


using System;

namespace Memento.RealWorld
{
    /// <summary>
    /// The 'Originator' class
    /// </summary>

    public class SalesProspect
    {
        string name;
        string phone;
        double budget;

        // Gets or sets name

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                Console.WriteLine("Name:   " + name);
            }
        }

        // Gets or sets phone

        public string Phone
        {
            get { return phone; }
            set
            {
                phone = value;
                Console.WriteLine("Phone:  " + phone);
            }
        }

        // Gets or sets budget

        public double Budget
        {
            get { return budget; }
            set
            {
                budget = value;
                Console.WriteLine("Budget: " + budget);
            }
        }

        // Stores memento

        public Memento SaveMemento()
        {
            Console.WriteLine("\nSaving state --\n");
            return new Memento(name, phone, budget);
        }

        // Restores memento

        public void RestoreMemento(Memento memento)
        {
            Console.WriteLine("\nRestoring state --\n");
            Name = memento.Name;
            Phone = memento.Phone;
            Budget = memento.Budget;
        }
    }

    /// <summary>
    /// The 'Memento' class
    /// </summary>

    public class Memento
    {
        string name;
        string phone;
        double budget;

        // Constructor

        public Memento(string name, string phone, double budget)
        {
            this.name = name;
            this.phone = phone;
            this.budget = budget;
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public string Phone
        {
            get { return phone; }
            set { phone = value; }
        }

        public double Budget
        {
            get { return budget; }
            set { budget = value; }
        }
    }

    /// <summary>
    /// The 'Caretaker' class
    /// </summary>

    public class ProspectMemory
    {
        Memento memento;

        public Memento Memento
        {
            set { memento = value; }
            get { return memento; }
        }
    }
	
	/// <summary>
    /// Memento Design Pattern
    /// </summary>

    public class Program
    {
        public static void Main(string[] args)
        {
            SalesProspect s = new SalesProspect();
            s.Name = "Noel van Halen";
            s.Phone = "(412) 256-0990";
            s.Budget = 25000.0;

            // Store internal state

            ProspectMemory m = new ProspectMemory();
            m.Memento = s.SaveMemento();

            // Continue changing originator

            s.Name = "Leo Welch";
            s.Phone = "(310) 209-7111";
            s.Budget = 1000000.0;

            // Restore saved state

            s.RestoreMemento(m.Memento);

            // Wait for user

            Console.ReadKey();
        }
    }
}

Output
Name:   Noel van Halen
Phone:  (412) 256-0990
Budget: 25000

Saving state --

Name:   Leo Welch
Phone:  (310) 209-7111
Budget: 1000000

Restoring state --

Name:   Noel van Halen
Phone:  (412) 256-0990
Budget: 25000

Class Diagram



6. State Design Pattern

State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.

Real-World Analogy
The buttons and switches in your smartphone behave differently depending on the current state of the device:
  1. When the phone is unlocked, pressing buttons leads to executing various functions.
  2. When the phone is locked, pressing any button leads to the unlock screen.
  3. When the phone’s charge is low, pressing any button shows the charging screen.

Real-world code in C#

This real-world code demonstrates the State pattern which allows an Account to behave differently depending on its balance. The difference in behavior is delegated to State objects called RedState, SilverState and GoldState. These states represent overdrawn accounts, starter accounts, and accounts in good standing.

Example

using System;

namespace State.RealWorld
{
    /// <summary>
    /// The 'State' abstract class
    /// </summary>

    public abstract class State
    {
        protected Account account;
        protected double balance;

        protected double interest;
        protected double lowerLimit;
        protected double upperLimit;

        // Properties

        public Account Account
        {
            get { return account; }
            set { account = value; }
        }

        public double Balance
        {
            get { return balance; }
            set { balance = value; }
        }

        public abstract void Deposit(double amount);
        public abstract void Withdraw(double amount);
        public abstract void PayInterest();
    }


    /// <summary>
    /// A 'ConcreteState' class
    /// <remarks>
    /// Red indicates that account is overdrawn 
    /// </remarks>
    /// </summary>

    public class RedState : State
    {
        private double serviceFee;

        // Constructor

        public RedState(State state)
        {
            this.balance = state.Balance;
            this.account = state.Account;
            Initialize();
        }

        private void Initialize()
        {
            // Should come from a datasource

            interest = 0.0;
            lowerLimit = -100.0;
            upperLimit = 0.0;
            serviceFee = 15.00;
        }

        public override void Deposit(double amount)
        {
            balance += amount;
            StateChangeCheck();
        }

        public override void Withdraw(double amount)
        {
            amount = amount - serviceFee;
            Console.WriteLine("No funds available for withdrawal!");
        }

        public override void PayInterest()
        {
            // No interest is paid
        }

        private void StateChangeCheck()
        {
            if (balance > upperLimit)
            {
                account.State = new SilverState(this);
            }
        }
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// <remarks>
    /// Silver indicates a non-interest bearing state
    /// </remarks>
    /// </summary>

    public class SilverState : State
    {
        // Overloaded constructors

        public SilverState(State state) :
            this(state.Balance, state.Account)
        {
        }

        public SilverState(double balance, Account account)
        {
            this.balance = balance;
            this.account = account;
            Initialize();
        }

        private void Initialize()
        {
            // Should come from a datasource
            interest = 0.0;
            lowerLimit = 0.0;
            upperLimit = 1000.0;
        }

        public override void Deposit(double amount)
        {
            balance += amount;
            StateChangeCheck();
        }

        public override void Withdraw(double amount)
        {
            balance -= amount;
            StateChangeCheck();
        }

        public override void PayInterest()
        {
            balance += interest * balance;
            StateChangeCheck();
        }

        private void StateChangeCheck()
        {
            if (balance < lowerLimit)
            {
                account.State = new RedState(this);
            }
            else if (balance > upperLimit)
            {
                account.State = new GoldState(this);
            }
        }
    }

    /// <summary>
    /// A 'ConcreteState' class
    /// <remarks>
    /// Gold indicates an interest bearing state
    /// </remarks>
    /// </summary>

    public class GoldState : State
    {
        // Overloaded constructors
        public GoldState(State state)
            : this(state.Balance, state.Account)
        {
        }

        public GoldState(double balance, Account account)
        {
            this.balance = balance;
            this.account = account;
            Initialize();
        }

        private void Initialize()
        {
            // Should come from a database
            interest = 0.05;
            lowerLimit = 1000.0;
            upperLimit = 10000000.0;
        }

        public override void Deposit(double amount)
        {
            balance += amount;
            StateChangeCheck();
        }

        public override void Withdraw(double amount)
        {
            balance -= amount;
            StateChangeCheck();
        }

        public override void PayInterest()
        {
            balance += interest * balance;
            StateChangeCheck();
        }

        private void StateChangeCheck()
        {
            if (balance < 0.0)
            {
                account.State = new RedState(this);
            }
            else if (balance < lowerLimit)
            {
                account.State = new SilverState(this);
            }
        }
    }

    /// <summary>
    /// The 'Context' class
    /// </summary>

    public class Account
    {
        private State state;
        private string owner;

        // Constructor

        public Account(string owner)
        {
            // New accounts are 'Silver' by default
            this.owner = owner;
            this.state = new SilverState(0.0, this);
        }

        public double Balance
        {
            get { return state.Balance; }
        }

        public State State
        {
            get { return state; }
            set { state = value; }
        }

        public void Deposit(double amount)
        {
            state.Deposit(amount);
            Console.WriteLine("Deposited {0:C} --- ", amount);
            Console.WriteLine(" Balance = {0:C}", this.Balance);
            Console.WriteLine(" Status  = {0}",
                this.State.GetType().Name);
            Console.WriteLine("");
        }

        public void Withdraw(double amount)
        {
            state.Withdraw(amount);
            Console.WriteLine("Withdrew {0:C} --- ", amount);
            Console.WriteLine(" Balance = {0:C}", this.Balance);
            Console.WriteLine(" Status  = {0}\n",
                this.State.GetType().Name);
        }

        public void PayInterest()
        {
            state.PayInterest();
            Console.WriteLine("Interest Paid --- ");
            Console.WriteLine(" Balance = {0:C}", this.Balance);
            Console.WriteLine(" Status  = {0}\n",
                this.State.GetType().Name);
        }
    }
	
	/// <summary>
    /// State Design Pattern
    /// </summary>

    public class Program
    {
        public static void Main(string[] args)
        {
            // Open a new account

            Account account = new Account("Jim Johnson");

            // Apply financial transactions

            account.Deposit(500.0);
            account.Deposit(300.0);
            account.Deposit(550.0);
            account.PayInterest();
            account.Withdraw(2000.00);
            account.Withdraw(1100.00);

            // Wait for user

            Console.ReadKey();
        }
    }
}


Output

Deposited $500.00 ---
 Balance = $500.00
 Status = SilverState


Deposited $300.00 ---
 Balance = $800.00
 Status = SilverState


Deposited $550.00 ---
 Balance = $1,350.00
 Status = GoldState


Interest Paid ---
 Balance = $1,417.50
 Status = GoldState

Withdrew $2,000.00 ---
 Balance = ($582.50)
 Status = RedState

No funds available for withdrawal!
Withdrew $1,100.00 ---
 Balance = ($582.50)
 Status = RedState


Class Diagram


7. 
Visitor Design Pattern

Visitor is a behavioral design pattern that lets you separate algorithms from the objects on which they operate.

In the Visitor Design Pattern, we use a Visitor object which changes the executing algorithm of an element object. In this way, when the visitor varies, the execution algorithm of the element object can also vary. As per the Visitor Design Pattern, the element object has to accept the visitor object so that the visitor object handles the operation on the element object.

The Visitor Design Pattern should be used when you have distinct and unrelated operations to perform across a structure of objects (element objects). This avoids adding in code throughout your object structure that is better kept separate.

When to use Visitor Design Pattern in C#?
  1. An object structure must have many unrelated operations to perform on it.
  2. An object structure cannot change but operations performed on it can change.
  3. The operations need to perform on the concrete classes of an object structure.
  4. Exposing the internal state or operations of the object structure is acceptable.
  5. Operations should be able to operate on multiple object structures that implement the same interface.
Real-Time Example of Visitor Design Pattern:

Please have a look at the following image. Here, we have a school and in school, lots of kids are studying. One day the school management has decided to perform a health checkup for all the kids. So, what the school management has done is they appointment one child specialist doctor. What the doctor will do is, he will visit the school and check the health of each and every kid. Once he has checked the health of each kid, then he provides the reports to the school management.

So, here in this example, the doctor is nothing but a visitor and the object structure is a collection of Kids where each kid is an element object.



In the same manner, one school bag sales company wants to promote their school bag. So, the company communicates with the school management and decided to give a school to each kid as a gift. So, the company salesman visits the school and give a school bag to each kid. For better understanding please have a look at the following diagram.

Here, the salesman is the visitor and the object structure is the same collection of kids where each kid is an element.

Code 

Creating Element 

namespace VisitorDesignPattern
{
   public interface IElement
    {
        void Accept(IVisitor visitor);
    }
	
    public class Kid : IElement
    {
        public string KidName { get; set; }
        
        public Kid(string name)
        {
            KidName = name;
        }
        
        public void Accept(IVisitor visitor)
        {
            visitor.Visit(this);
        }
    }
} 


Creating Visitor
namespace VisitorDesignPattern
{
    public interface IVisitor
    {
        void Visit(IElement element);
    }
	
    public class Doctor : IVisitor
    {
        public string Name { get; set; }
        public Doctor(string name)
        {
            Name = name;
        }
        
        public void Visit(IElement element)
        {
            Kid kid = (Kid)element;
            Console.WriteLine("Doctor: " + this.Name+ " did the health checkup of the child: "+ kid.KidName);
        }
    }
	
    class Salesman : IVisitor
    {
        public string Name { get; set; }
        public Salesman(string name)
        {
            Name = name;
        }
        public void Visit(IElement element)
        {
            Kid kid = (Kid)element;
            Console.WriteLine("Salesman: " + this.Name + " gave the school bag to the child: "
                            + kid.KidName);
        }
    }
}

Creating Object Structure
using System.Collections.Generic;
namespace VisitorDesignPattern
{
    public class School
    {
        private static List<IElement> elements;
        static School()
        {
            elements = new List<IElement>
            {
                new Kid("Ram"),
                new Kid("Sara"),
                new Kid("Pam")
            };
        }
        public void PerformOperation(IVisitor visitor)
        {
            foreach (var kid in elements)
            {
                kid.Accept(visitor);
            }
        }
    }
}


Client Code
using System;
namespace VisitorDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            School school = new School();

            var visitor1 = new Doctor("James");
            school.PerformOperation(visitor1);
            Console.WriteLine();

            var visitor2 = new Salesman("John");
            school.PerformOperation(visitor2);

            Console.Read();
        }
    }
}


Output
Doctor: James did the health checkup of the child: Ram
Doctor: James did the health checkup of the child: Sara
Doctor: James did the health checkup of the child: Pam

Salesman: John gave the school bag to the child: Ram
Salesman: John gave the school bag to the child: Sara
Salesman: John gave the school bag to the child: Pam

Class Diagram


8. Template Design Pattern

Template Method is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

The Key to the Template Design Pattern is that we put the general logic in the abstract parent class and let the child classes define the specifics.

Realtime Scenario:
Let us understand the above explanation with an example. Suppose you want to build a concrete house, then you need to follow a sequence of steps such as first you need to build a foundation, then you need to build Pillars. Third, you need to build Walls and finally, you need to build Windows as shown in the below image.



So, you have to follow the above sequence of procedures in order to build a concrete house. Suppose you want to build a wooden house. Then also you need to follow the same sequence of procedures that you follow in order to build a concrete house i.e. first you need to build the foundation, then you need to build Pillars, then Walls, and Finally Windows as shown in the below image.




The only difference here is instead of using Concrete you need to use Wooden Materials. So, what you can do here is, you can define a template method (called Build a House). The template method will define what are all the procedures or steps (i.e. step1: Building Foundation, Step2: Building Pillars, Step3: Building Walls, Step4: Building Windows) that you need to follow in order to build a house. So, for both the house (Concrete and Wooden) you need to above steps which are shown in the below image.

Code:

Creating the Template Method in Abstract class

using System;
namespace TemplateMethodDesignPattern
{
    public abstract class HouseTemplate
    {
        // Template method defines the sequence for building a house
        public void BuildHouse()
        {
            BuildFoundation();
            BuildPillars();
            BuildWalls();
            BuildWindows();
            Console.WriteLine("House is built");
        }

        // Methods to be implemented by subclasses
        protected abstract void BuildFoundation();
        protected abstract void BuildPillars();
        protected abstract void BuildWalls();
        protected abstract void BuildWindows();
    }
}


Creating Concrete Class.
using System;
namespace TemplateMethodDesignPattern
{
    public class ConcreteHouse : HouseTemplate
    {
        protected override void BuildFoundation()
        {
            Console.WriteLine("Building foundation with cement, iron rods and sand");
        }

        protected override void BuildPillars()
        {
            Console.WriteLine("Building Concrete Pillars with Cement and Sand");
        }

        protected override void BuildWalls()
        {
            Console.WriteLine("Building Concrete Walls");
        }

        protected override void BuildWindows()
        {
            Console.WriteLine("Building Concrete Windows");
        }
    }
}

Creating Wooden Class.
using System;
namespace TemplateMethodDesignPattern
{
    public class WoodenHouse : HouseTemplate
    {
        protected override void BuildFoundation()
        {
            Console.WriteLine("Building foundation with cement, iron rods, wood and sand");
        }

        protected override void BuildPillars()
        {
            Console.WriteLine("Building wood Pillars with wood coating");
        }

        protected override void BuildWalls()
        {
            Console.WriteLine("Building Wood Walls");
        }

        protected override void BuildWindows()
        {
            Console.WriteLine("Building Wood Windows");
        }
    }
}

Client
using System;
namespace TemplateMethodDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Build a Concrete House\n");
            HouseTemplate houseTemplate = new ConcreteHouse();
            // call the template method
            houseTemplate.BuildHouse();

            Console.WriteLine();

            Console.WriteLine("Build a Wooden House\n");
            houseTemplate = new WoodenHouse();
            // call the template method
            houseTemplate.BuildHouse();

            Console.Read();
        }
    }
}

Output:

Build a Concrete House

Building foundation with cement, iron rods and sand
Building Concrete Pillars with Cement and Sand
Building Concrete Walls
Building Concrete Windows
House is built

Build a Wooden House

Building foundation with cement, iron rods, wood and sand
Building wood Pillars with wood coating
Building Wood Walls
Building Wood Windows
House is built


Class Diagram:




No comments:

Post a Comment