Behavioral Design Pattern for .NET: Part 2


BEHAVIORAL:

Here is Part 1

Behavioral Design patterns are the patterns for .Net in which there is a way through which we can pass the request between the chain of objects, or we can say that it defines the manner to communicate between classes and object.

Behavioral Patterns are Categorized as under:

BehaPatte1.gif


6. Memento:

This pattern proves to be helpful in saving the internal state of the object in to external state and enabling us to restore the state later when needed means it easily restore an object to its previous state with minimal lines of coding. Memento pattern helps us to store a snapshot which can be reverted at any moment of time by the object. A word processor providing undo operation or Ctrl+Z is the example of this pattern.

Model:

BehaPatte2.gif

UML Diagram:

BehaPatte3.gif

Example:

using System;
using System.Data;

namespace Memento
{
 class Example
  {
   static void Main()
    {
     Originator o = new Originator();
     o.State = "On";
     Caretaker c = new Caretaker();      // Store internal state
     c.Memento = o.CreateMemento();      
     o.State = "Off";                           // Continue changing originator

      // Restore saved state
     o.SetMemento(c.Memento);
     Console.ReadKey();
    }
  }

 class Originator
  {
   private string _state;
   public string State
    {
     get { return _state; }
     set
      {
       _state = value;
       Console.WriteLine("State = " + _state);
      }
   }

   // Creates memento
  public Memento CreateMemento()
    {
     return (new Memento(_state));
    }
  public void SetMemento(Memento memento)
    {
     Console.WriteLine("Restoring state...");
     State = memento.State;
    }
  }

 class Memento
  {
   private string _state;
   public Memento(string state)
    {
     this._state = state;
    }
   public string State
    {
     get { return _state; }
    }
  }

 class Caretaker            //The 'Caretaker' class
    {
     private Memento _memento;
     public Memento Memento
      {
       set { _memento = value; }
       get { return _memento; }
      }
     }
 }

7. Observer:

This pattern allows us to plug objects into a framework at runtime, which allows for highly flexible, extensible, and reusable software. It is used when it is necessary to ensure that multiple components (called observers or subscribers) are kept in sync with a master set of data (the subject or publisher).

Model:

BehaPatte4.gif

UML Diagram:

Example:

BehaPatte5.gif

using System;
using System.Collections.Generic;

namespace Observer
{
 class Example
  {
   static void Main()
    {
     ConcreteSubject s = new ConcreteSubject();
      s.Attach(new ConcreteObserver(s, "X"));
      s.Attach(new ConcreteObserver(s, "Y"));
      s.Attach(new ConcreteObserver(s, "Z"));
      s.SubjectState = "ABC";
      s.Notify();
     Console.ReadKey();
    }
  }

 abstract class Subject
  {
   private List<Observer> _observers = new List<Observer>();
   public void Attach(Observer observer)
    {
     _observers.Add(observer);
    }
   public void Detach(Observer observer)
    {
     _observers.Remove(observer);
    }
   public void Notify()
    {
     foreach (Observer o in _observers)
      {
       o.Update();
      }
    }
  }

 class ConcreteSubject : Subject
  {
   private string _subjectState;
   public string SubjectState
    {
     get { return _subjectState; }
     set { _subjectState = value; }
    }
  }

 abstract class Observer
  {
   public abstract void Update();
  }
 class ConcreteObserver : Observer
  {
     private string _name;
     private string _observerState;
     private ConcreteSubject _subject;
     public ConcreteObserver(
 ConcreteSubject subject, string name)
    {
     this._subject = subject;
     this._name = name;
    }
 public override void Update()
    {
     _observerState = _subject.SubjectState;
     Console.WriteLine("Observer {0}'s new state is {1}",
     _name, _observerState);
    }
 public ConcreteSubject Subject
    {
     get { return _subject; }
     set { _subject = value; }
    }
  }
}

8. State:

State Pattern allows an object to alter its behavior dynamically according to its internal state. For this we directly move the logic to property "State" which has method Handle() and call this method. Vending Machine is one of the example of it:

Model:

BehaPatte6.gif

UML Diagram:

BehaPatte7.gif

Example:

using System;
using System.Data;
using System.Collections.Generic;

namespace State
{
 class Example
  {
   static void Main()
    {

      // Open a new account
     Account account = new Account("Mr.Chabbra");
     
      // 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);
     Console.ReadKey();
    }
  }

 abstract class State
  {
   protected Account account;
   protected double balance;
   protected double interest;
   protected double lowerLimit;
   protected double upperLimit;
   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();
  }

//Red indicates that account is overdrawn 
class RedState : State
  {
   private double _serviceFee;
   public RedState(State state)
    {
     this.balance = state.Balance;
     this.account = state.Account;
     Initialize();
    }
   private void Initialize()            // 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);
      }
     }
  }


  //Silver indicates a non-interest bearing state

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

//Gold indicates an interest bearing state 
class GoldState : State
   {
     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()
      {
       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);
        }
     }
  }

 class Account
  {
   private State _state;
   private string _owner;
   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);
    }
  }
}

9. Strategy:

The strategy pattern is very useful when we are in a need of a dynamic formula or data. It is used to separate different types of algorithms that serve a similar purpose. It's a strategy of dealing with a code design with a purpose to decouple concrete code into separate classes, which promotes reusability. Modes of transportation to Airport is a line example of Strategy Pattern:

Model:

BehaPatte8.gif


UML Diagram:

BehaPatte9.gif

Example:

using System;
using System.Collections.Generic;
using System.Data;

namespace Strategy
{
 class Example
  {
   static void Main()
    {
   // Two contexts following different strategies
     SortedList clientRecords = new SortedList();
     clientRecords.Add("Rahul");
     clientRecords.Add("John");
     clientRecords.Add("Vikky");

     clientRecords.SetSortStrategy(new QuickSort());
     clientRecords.Sort();
     clientRecords.SetSortStrategy(new ShellSort());
     clientRecords.Sort();
     clientRecords.SetSortStrategy(new MergeSort());
     clientRecords.Sort();
     Console.ReadKey();

    }
 }

  abstract class SortStrategy
  {
   public abstract void Sort(List<string> list);
  }
 
 class QuickSort : SortStrategy
  {
   public override void Sort(List<string> list)
    {
     list.Sort();                               // Default is Quicksort
     Console.WriteLine("QuickSorted list ");
    }
  }

 class ShellSort : SortStrategy
  {
   public override void Sort(List<string> list)
    {
     Console.WriteLine("ShellSorted list ");
    }
  }

 class MergeSort : SortStrategy
  {
   public override void Sort(List<string> list)
    {
     Console.WriteLine("MergeSorted list ");
    }
  }

 class SortedList
  {
   private List<string> _list = new List<string>();
   private SortStrategy _sortstrategy;
   public void SetSortStrategy(SortStrategy sortstrategy)
    {
     this._sortstrategy = sortstrategy;
    }
   public void Add(string name)
    {
     _list.Add(name);
    }
   public void Sort()
    {
     _sortstrategy.Sort(_list);

      // Iterate over list and display results
     foreach (string name in _list)
       {
        Console.WriteLine(" " + name);
       }
        Console.WriteLine();
     }
  }
}

10. Template Method:

This pattern helps us to improve code reusability. It lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. We cannot override the template method. Only the steps given in the skeleton of algorithm of template method need to be overridden in concrete classes.

Model:

BehaPatte10.gif

UML Diagram:

BehaPatte11.gif


Example:

using System;
using System.Data;
using System.Data.OleDb;

namespace TemplateMethod
{
 class Example
  {
   static void Main()
    {
     AbstractClass aA = new ConcreteClassA();
     aA.TemplateMethod();
     AbstractClass aB = new ConcreteClassB();
     aB.TemplateMethod();
     Console.ReadKey();
    }
  }

 abstract class AbstractClass
  {
   public abstract void PrimitiveOperation1();
   public abstract void PrimitiveOperation2();

   // The "Template method"
   public void TemplateMethod()
    {
      PrimitiveOperation1();
      PrimitiveOperation2();
      Console.WriteLine("");
    }
  }

 class ConcreteClassA : AbstractClass                  //A 'ConcreteClass' class
  {
   public override void PrimitiveOperation1()
    {
     Console.WriteLine("ConcreteClassA.PrimitiveOperation1()");
    }
   public override void PrimitiveOperation2()
    {
     Console.WriteLine("ConcreteClassA.PrimitiveOperation2()");
    }
  }

 class ConcreteClassB : AbstractClass                        //A 'ConcreteClass' class
  {
   public override void PrimitiveOperation1()
    {
     Console.WriteLine("ConcreteClassB.PrimitiveOperation1()");
    }
   public override void PrimitiveOperation2()
    {
     Console.WriteLine("ConcreteClassB.PrimitiveOperation2()");
    }
  }
}

11. Visitor:

With the help of visitor pattern the new operations are performed on an existing structure. The visitor pattern also specifies how iteration occurs over the object structure. It is a classic technique for recovering lost type information. Operation of a Taxi Company is a good example of Visitor pattern.

Model:

BehaPatte12.gif

UML Diagram:

BehaPatte13.gif

Example:

using System;
using System.Collections.Generic;

namespace Visitor
{
 class Example
  {
   static void Main()
    {
      // Setup employee collection
     Employees e = new Employees();
     e.Attach(new Designer());
     e.Attach(new Developer());
     e.Attach(new HRManager());

      // Employees are 'visited'
     e.Accept(new IncomeVisitor());
     e.Accept(new VacationVisitor());
     Console.ReadKey();
    }
  }
 interface IVisitor                     //The 'Visitor' interface
  {
   void Visit(Element element);
  }
   class IncomeVisitor : IVisitor
    {
     public void Visit(Element element)
      {
       Employee employee = element as Employee;

       // Provide 10% pay raise
       employee.Income *= 1.10;
       Console.WriteLine("{0} {1}'s new income: {2:C}",
       employee.GetType().Name, employee.Name,
       employee.Income);
      }
    }

 class VacationVisitor : IVisitor
  {
   public void Visit(Element element)
    {
     Employee employee = element as Employee;

     // Provide 3 extra vacation days
     Console.WriteLine("{0} {1}'s vacation days for this year: {2}",
     employee.GetType().Name, employee.Name,
     employee.VacationDays);
    }
  }

 abstract class Element
  {
   public abstract void Accept(IVisitor visitor);
  }

 class Employee : Element
  {
   private string _name;
   private double _income;
   private int _vacationDays;
   public Employee(string name, double income,
   int vacationDays)
    {
     this._name = name;
     this._income = income;
     this._vacationDays = vacationDays;
    }
   public string Name
    {
     get { return _name; }
     set { _name = value; }
    }
   public double Income
    {
     get { return _income; }
     set { _income = value; }
    }
   public int VacationDays
    {
     get { return _vacationDays; }
     set { _vacationDays = value; }
    }
   public override void Accept(IVisitor visitor)
    {
     visitor.Visit(this);
    }
  }

class Employees                                 //The 'ObjectStructure' class
 {
  private List<Employee> _employees = new List<Employee>();
  public void Attach(Employee employee)
   {
    _employees.Add(employee);
   }
  public void Detach(Employee employee)
   {
    _employees.Remove(employee);
   }
  public void Accept(IVisitor visitor)
   {
    foreach (Employee e in _employees)
      {
       e.Accept(visitor);
      }
    Console.WriteLine();
   }
  }

// Three employee types 
class Designer : Employee
  {
   public Designer()
   : base("MUKUL", 30000.0, 11)
     {    }
  }

 class Developer : Employee
  {
   public Developer()
   : base("Deepak", 45000.0, 12)
     {    }
  }

 class HRManager : Employee
  {
   public HRManager()
   : base("Rohit", 50000.0, 20)
     {    }
  }
}


Similar Articles