0% found this document useful (0 votes)
83 views

Behavioural Design Patterns

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. It encapsulates core components in a Subject abstraction and variable components in Observer objects. When the subject's state changes, it notifies all registered observer objects so they can update themselves. For example, in an auction each bidder observes the current bid and updates it when the auctioneer changes the bid price.

Uploaded by

Akula Sandeep
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
83 views

Behavioural Design Patterns

The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. It encapsulates core components in a Subject abstraction and variable components in Observer objects. When the subject's state changes, it notifies all registered observer objects so they can update themselves. For example, in an auction each bidder observes the current bid and updates it when the auctioneer changes the bid price.

Uploaded by

Akula Sandeep
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

Page 1 of 21

Observer Design Pattern

Intent

 Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified
and updated automatically.
 Encapsulate the core (or common or engine) components in a Subject abstraction, and the variable (or optional or user
interface) components in an Observer hierarchy.
 The "View" part of Model-View-Controller.

The Observer defines a one-to-many relationship so that when one object changes state, the others are notified and
updated automatically. Some auctions demonstrate this pattern. Each bidder possesses a numbered paddle that is used to
indicate a bid. The auctioneer starts the bidding, and "observes" when a paddle is raised to accept the bid. The acceptance
of the bid changes the bid price which is broadcast to all of the bidders in the form of a new bid.
Page 2 of 21
Check list

1. Differentiate between the core (or independent) functionality and the optional (or dependent) functionality.
2. Model the independent functionality with a "subject" abstraction.
3. Model the dependent functionality with an "observer" hierarchy.
4. The Subject is coupled only to the Observer base class.
5. The client configures the number and type of Observers.
6. Observers register themselves with the Subject.
7. The Subject broadcasts events to all registered Observers.
8. The Subject may "push" information at the Observers, or, the Observers may "pull" the information they need from the
Subject.

Example in Java
abstract class Observer {
protected Subject subj;
public abstract void update();
}

class HexObserver extends Observer {


public HexObserver( Subject s ) {
subj = s;
subj.attach( this );
}

public void update() {


System.out.print( " " + Integer.toHexString( subj.getState() ) );
}
} // Observers "pull" information

class OctObserver extends Observer {


public OctObserver( Subject s ) {
subj = s;
subj.attach( this );
}
public void update() {
System.out.print( " " + Integer.toOctalString( subj.getState() ) );
}
} // Observers "pull" information

class BinObserver extends Observer {


public BinObserver( Subject s ) {
subj = s;
subj.attach( this ); } // Observers register themselves
public void update() {
System.out.print( " " + Integer.toBinaryString( subj.getState() ) );
} }

class Subject {
private Observer[] observers = new Observer[9];
private int totalObs = 0;
private int state;
public void attach( Observer o ) {
observers[totalObs++] = o;
}

public int getState() {


return state;
}

public void setState( int in ) {


state = in;
notify();
}

private void notify() {


for (int i=0; i < totalObs; i++) {
observers[i].update();
} }}
Page 3 of 21
public class ObserverDemo {
public static void main( String[] args ) {
Subject sub = new Subject();
// Client configures the number and type of Observers
new HexObserver( sub );
new OctObserver( sub );
new BinObserver( sub );
Scanner scan = new Scanner();
while (true) {
System.out.print( "\nEnter a number: " );
sub.setState( scan.nextInt() );
}
}
}
Enter a number: 15
f 17 1111
Enter a number: 17
11 21 10001
Enter a number: 31
1f 37 11111

Example in C#
using System;
using System.Collections;

class MainApp
{
static void Main()
{
// Configure Observer pattern
ConcreteSubject s = new ConcreteSubject();

s.Attach(new ConcreteObserver(s,"X"));
s.Attach(new ConcreteObserver(s,"Y"));
s.Attach(new ConcreteObserver(s,"Z"));

// Change subject and notify observers


s.SubjectState = "ABC";
s.Notify();

// Wait for user


Console.Read();
}
}

// "Subject"
abstract class Subject
{
private ArrayList observers = new ArrayList();

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

// "ConcreteSubject"
class ConcreteSubject : Subject
Page 4 of 21
{
private string subjectState;

// Property
public string SubjectState
{
get{ return subjectState; }
set{ subjectState = value; }
}
}

// "Observer"
abstract class Observer
{
public abstract void Update();
}

// "ConcreteObserver"
class ConcreteObserver : Observer
{
private string name;
private string observerState;
private ConcreteSubject subject;

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

// Property
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
Observer X's new state is ABC
Observer Y's new state is ABC
Observer Z's new state is ABC
Page 5 of 21

State Design Pattern


Definition
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

UML class diagram

Participants

The classes and objects participating in this pattern are:

 Context (Account)
o defines the interface of interest to clients
o maintains an instance of a ConcreteState subclass that defines the current state.
 State (State)
o defines an interface for encapsulating the behavior associated with a particular state of the Context.
 Concrete State (RedState, SilverState, GoldState)
o each subclass implements a behavior associated with a state of Context

Structural code in C#

This structural code demonstrates the State pattern which allows an object to behave differently depending on its internal
state. The difference in behavior is delegated to objects that represent this state.

using System;

namespace DoFactory.GangOfFour.State.Structural
{
/// <summary>
/// MainApp startup class for Structural
/// State Design Pattern.
/// </summary>
class MainApp
{
/// <summary>
/// Entry point into console application.
/// </summary>
static void Main()
{
// Setup context in a state
Context c = new Context(new ConcreteStateA());

// Issue requests, which toggles state


c.Request();
c.Request();
c.Request();
c.Request();
// Wait for user
Console.ReadKey(); } }
Page 6 of 21
/// <summary>
/// The 'State' abstract class
/// </summary>
abstract class State
{
public abstract void Handle(Context context);
}

/// <summary>
/// A 'ConcreteState' class
/// </summary>
class ConcreteStateA : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateB();
} }

/// <summary>
/// A 'ConcreteState' class
/// </summary>
class ConcreteStateB : State
{
public override void Handle(Context context)
{
context.State = new ConcreteStateA();
} }

/// <summary>
/// The 'Context' class
/// </summary>
class Context
{
private State _state;

// Constructor
public Context(State state)
{
this.State = state;
}

// Gets or sets the state


public State State
{
get { return _state; }
set
{
_state = value;
Console.WriteLine("State: " +
_state.GetType().Name);
}
}

public void Request()


{
_state.Handle(this);
}
}
}

Output
State: ConcreteStateA
State: ConcreteStateB
State: ConcreteStateA
State: ConcreteStateB
State: ConcreteStateA
Page 7 of 21

Strategy Design Pattern


Motivation
There are common situations when classes differ only in their behavior. For this cases is a good idea to isolate the algorithms in
separate classes in order to have the ability to select different algorithms at runtime.

Intent
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary
independently from clients that use it.

Implementation

Strategy - defines an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a
ConcreteStrategy.

ConcreteStrategy - each concrete strategy implements an algorithm.

Context

 contains a reference to a strategy object.


 may define an interface that lets strategy accessing its data.

The Context objects contains a reference to the ConcreteStrategy that should be used. When an operation is required then the
algorithm is run from the strategy object. The Context is not aware of the strategy implementation. If necessary, addition objects
can be defined to pass data from context object to strategy.
The context object receives requests from the client and delegates them to the strategy object. Usually the ConcreteStartegy is
created by the client and passed to the context. From this point the clients interacts only with the context.

Applicability & Examples

Example - Robots Application


Page 8 of 21

Let's consider an application used to simulate and study robots interaction. For the beginning a simple application is created to
simulate an arena where robots are interacting. We have the following classes:

IBehaviour (Strategy) - an interface that defines the behavior of a robot

Conctete Strategies: AggressiveBehaviour, DefensiveBehaviour, NormalBehaviour; each of them defines a specific behavior. In
order to decide the action this class needs information that is passed from robot sensors like position, close obstacles, etc.

Robot - The robot is the context class. It keeps or gets context information such as position, close obstacles, etc, and passes
necessary information to the Strategy class.

In the main section of the application the several robots are created and several different behaviors are created. Each robot has a
different behavior assigned: 'Big Robot' is an aggressive one and attacks any other robot found, 'George v.2.1' is really scared and
run away in the opposite direction when it encounter another robot and 'R2' is pretty calm and ignore any other robot. At some
point the behaviors are changed for each robot.

public interface IBehaviour {


public int moveCommand();
}

public class AgressiveBehaviour implements IBehaviour{


public int moveCommand()
{
System.out.println("\tAgressive Behaviour: if find another robot attack it");
return 1;
}}

public class DefensiveBehaviour implements IBehaviour{


public int moveCommand()
{
System.out.println("\tDefensive Behaviour: if find another robot run from it");
return -1;
}}

public class NormalBehaviour implements IBehaviour{


public int moveCommand()
{
System.out.println("\tNormal Behaviour: if find another robot ignore it");
return 0;
}}

public class Robot {


IBehaviour behaviour;
String name;

public Robot(String name)


{ this.name = name; }

public void setBehaviour(IBehaviour behaviour)


{ this.behaviour = behaviour; }

public IBehaviour getBehaviour()


{ return behaviour; }

public void move()


Page 9 of 21
{
System.out.println(this.name + ": Based on current position" +
"the behaviour object decide the next move:");
int command = behaviour.moveCommand();
// ... send the command to mechanisms
System.out.println("\tThe result returned by behaviour object " +
"is sent to the movement mechanisms " +
" for the robot '" + this.name + "'");
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}}

public class Main {

public static void main(String[] args) {

Robot r1 = new Robot("Big Robot");


Robot r2 = new Robot("George v.2.1");
Robot r3 = new Robot("R2");

r1.setBehaviour(new AgressiveBehaviour());
r2.setBehaviour(new DefensiveBehaviour());
r3.setBehaviour(new NormalBehaviour());

r1.move();
r2.move();
r3.move();

System.out.println("\r\nNew behaviours: " +


"\r\n\t'Big Robot' gets really scared" +
"\r\n\t, 'George v.2.1' becomes really mad because" +
"it's always attacked by other robots" +
"\r\n\t and R2 keeps its calm\r\n");

r1.setBehaviour(new DefensiveBehaviour());
r2.setBehaviour(new AgressiveBehaviour());

r1.move();
r2.move();
r3.move();
}
}

Specific problems and implementation


Passing data to/from Strategy object
Usually each strategy need data from the context have to return some processed data to the context. This can be achieved in 2
ways.
Page 10 of 21
 creating some additional classes to encapsulate the specific data.
 passing the context object itself to the strategy objects. The strategy object can set returning data directly in the context.

When data should be passed the drawbacks of each method should be analyzed. For example, if some classes are created to
encapsulate additional data, a special care should be paid to what fields are included in the classes. Maybe in the current
implementation all required fields are added, but maybe in the future some new strategy concrete classes require data from
context which are not include in additional classes. Another fact should be specified at this point: it's very likely that some of the
strategy concrete classes will not use field passed to the in the additional classes.

On the other side, if the context object is passed to the strategy then we have a tighter coupling between strategy and context.

Families of related algorithms.


The strategies can be defined as a hierarchy of classes offering the ability to extend and customize the existing algorithms from an
application. At this point the composite design pattern can be used with a special care.

Optionally Concrete Strategy Objects


It's possible to implement a context object that carries an implementation for default or a basic algorithm. While running it, it
checks if it contains a strategy object. If not it will run the default code and that's it. If a strategy object is found, it is called instead
(or in addition) of the default code. This is an elegant solution to exposing some customization points to be used only when they
are required. Otherwise the clients don't have to deal with Strategy objects.

Strategy and Creational Patterns


In the classic implementation of the pattern the client should be aware of the strategy concrete classes. In order to decouple the
client class from strategy classes is possible to use a factory class inside the context object to create the strategy object to be used.
By doing so the client has only to send a parameter (like a string) to the context asking to use a specific algorithm, being totally
decoupled of strategy classes.

Strategy and Bridge


Both of the patterns have the same UML diagram. But they differ in their intent since the strategy is related with the behavior and
bridge is for structure. Further more, the coupling between the context and strategies is tighter than the coupling between the
abstraction and implementation in the bridge pattern.
Page 11 of 21
Real-world code in C#

This real-world code demonstrates the Strategy pattern which encapsulates sorting algorithms in the form of sorting
objects. This allows clients to dynamically change sorting strategies including Quicksort, Shellsort, and Mergesort.

1. using System;
2. using System.Collections.Generic;
3. namespace DoFactory.GangOfFour.Strategy.RealWorld
4. { /// <summary>
5. /// MainApp startup class for Real-World
6. /// Strategy Design Pattern.
7. /// </summary>
8. class MainApp
9. {
10. /// <summary>
11. /// Entry point into console application.
12. /// </summary>
13. static void Main()
14. {
15. // Two contexts following different strategies
16. SortedList studentRecords = new SortedList();
17.
18. studentRecords.Add("Samual");
19. studentRecords.Add("Jimmy");
20. studentRecords.Add("Sandra");
21. studentRecords.Add("Vivek");
22. studentRecords.Add("Anna");
23.
24. studentRecords.SetSortStrategy(new QuickSort());
25. studentRecords.Sort();
26.
27. studentRecords.SetSortStrategy(new ShellSort());
28. studentRecords.Sort();
29.
30. studentRecords.SetSortStrategy(new MergeSort());
31. studentRecords.Sort();
32.
33. // Wait for user
34. Console.ReadKey();
35. }
36. }
37.
38. /// <summary>
39. /// The 'Strategy' abstract class
40. /// </summary>
41. abstract class SortStrategy
42. { public abstract void Sort(List<string> list); }
43.
44. /// <summary>
45. /// A 'ConcreteStrategy' class
46. /// </summary>
47. class QuickSort : SortStrategy
48. {
49. public override void Sort(List<string> list)
50. {
51. list.Sort(); // Default is Quicksort
52. Console.WriteLine("QuickSorted list ");
53. } }
54.
55. /// <summary>
56. /// A 'ConcreteStrategy' class
57. /// </summary>
58. class ShellSort : SortStrategy
59. {
60. public override void Sort(List<string> list)
61. {
62. //list.ShellSort(); not-implemented
63. Console.WriteLine("ShellSorted list ");
64. } }
65.
Page 12 of 21
66. /// <summary>
67. /// A 'ConcreteStrategy' class
68. /// </summary>
69. class MergeSort : SortStrategy
70. {
71. public override void Sort(List<string> list)
72. {
73. //list.MergeSort(); not-implemented
74. Console.WriteLine("MergeSorted list ");
75. } }
76.
77. /// <summary>
78. /// The 'Context' class
79. /// </summary>
80. class SortedList
81. {
82. private List<string> _list = new List<string>();
83. private SortStrategy _sortstrategy;
84.
85. public void SetSortStrategy(SortStrategy sortstrategy)
86. {
87. this._sortstrategy = sortstrategy;
88. }
89.
90. public void Add(string name)
91. {
92. _list.Add(name);
93. }
94.
95. public void Sort()
96. {
97. _sortstrategy.Sort(_list);
98.
99. // Iterate over list and display results
100. foreach (string name in _list)
101. {
102. Console.WriteLine(" " + name);
103. }
104. Console.WriteLine();
105. } }}
106.

Output

QuickSorted list
Anna
Jimmy
Samual
Sandra
Vivek

ShellSorted list
Anna
Jimmy
Samual
Sandra
Vivek

MergeSorted list
Anna
Jimmy
Samual
Sandra
Vivek
Page 13 of 21

Template Method Pattern


The template method pattern is a behavioral design pattern which provides a base method for an algorithm, called a template
method which defers some of its steps to subclasses. So the algorithm structure is the same but some of its steps can be
redefined by the subclasses according to the context.

Template means Preset format like HTML templates which have a fixed preset format. Similarly in the template method
pattern, we have a preset structure method called template method which consists of steps. These steps can be an abstract
method which will be implemented by its subclasses.

So in short you can say, in the template method pattern, there is a template method which defines a set of steps and the
implementation of steps can be deferred to subclasses. Thus a template method defines an algorithm but the exact steps can
be defined in subclasses.

When to use it:


When you have a preset format or steps for an algorithm but implementation of steps may vary.
When you want to avoid code duplication, implementing a common code in the base class and variations in subclass.
Structure

So in the above diagram, as you can see we have defined a template method with three steps: operation1, operation2, and
operation3. Among them, opeation1 and operation2 are abstract steps, so these are implemented byConcreteClass. We
have implemented operation3 here. You can implement an operation in a base class in two scenarios: first is if it is common to
all, and second is if it is the default implementation of that method. The UML diagram will be much clearer now.
Page 14 of 21

Components:
AbstractClass

It defines a template method defining the structure of an algorithm.


It also defines abstract operations that will be implemented by subclasses to define the steps of an algorithm.
ConcreteClass

It implements an abstract operation of a super class to carry out subclass specific steps of the algorithm and also overrides an
operation if the default behavior is not required
Important points about template method pattern:
The template method in a super class follows “the Hollywood principle”: “Don't call us, we'll call you”. This refers to the fact
that instead of calling the methods from the base class in the subclasses, the methods from the subclass are called in the
template method from the superclass.
Template method in the super class should not be overridden so make it final
Customization hooks: Methods containing a default implementation that may be overridden in other classes are called hook
methods. Hook methods are intended to be overridden, concrete methods are not. So in this pattern, we can provide hook
methods. The problem is sometimes it becomes very hard to differentiate between hook methods and concrete methods.
Template methods are techniques for code reuse because with this, you can figure out a common behavior and defer specific
behavior to subclasses.
Example
Let's take an example. When you have to read from two data sources, e.g., CSV and database, then you have to process that
data and generate the output as CSV files. Here three steps are involved.
Read data from the corresponding data source
Process data
Write output to CSV files
Page 15 of 21
Java code
The class below contains a template method called parseDataAndGenerateOutput which consists of steps for reading data,
processing data, and writing to a CSV file.

1.DataParser.java

package org.arpit.javapostsforlearning;
abstract public class DataParser {

//Template method
//This method defines a generic structure for parsing data
public void parseDataAndGenerateOutput()
{
readData();
processData();
writeData();
}
//This methods will be implemented by its subclass
abstract void readData();
abstract void processData();

//We have to write output in a CSV file so this step will be same for all subclasses
public void writeData()
{
System.out.println("Output generated,writing to CSV");
}
}

In the below class, CSV specific steps are implemented in this class.

2.CSVDataParser.java

package org.arpit.javapostsforlearning;
public class CSVDataParser extends DataParser {

void readData() {
System.out.println("Reading data from csv file");
}
void processData() {
System.out.println("Looping through loaded csv file");
}
}

In the below class, database specific steps are implemented.

3. DatabaseDataParser.java
package org.arpit.javapostsforlearning;
public class DatabaseDataParser extends DataParser {

void readData() {
System.out.println("Reading data from database");
}

void processData() {
System.out.println("Looping through datasets");
}
}

4. TemplateMethodMain.java
package org.arpit.javapostsforlearning;
public class TemplateMethodMain {

/**
* @author arpit mandliya
*/
public static void main(String[] args) {

CSVDataParser csvDataParser=new CSVDataParser();


csvDataParser.parseDataAndGenerateOutput();
Page 16 of 21
System.out.println("**********************");
DatabaseDataParser databaseDataParser=new DatabaseDataParser();
databaseDataParser.parseDataAndGenerateOutput();
}
}

Output:

Reading data from csv file


Looping through loaded csv file
Output generated, writing to CSV
**********************
Reading data from database
Looping through datasets
Output generated,writing to CSV

o Strategy is like Template Method except in its granularity.


o Template Method uses inheritance to vary part of an algorithm. Strategy uses delegation to
vary the entire algorithm.
o Strategy modifies the logic of individual objects. Template Method modifies the logic of an
entire class.
o Factory Method is a specialization of Template Method.
Page 17 of 21
Chain of Responsibility Design Pattern

definition
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the
request. Chain the receiving objects and pass the request along the chain until an object handles it.

UML class diagram

participants

The classes and/or objects participating in this pattern are:

 Handler (Approver)
o defines an interface for handling the requests
o (optional) implements the successor link
 ConcreteHandler (Director, VicePresident, President)
o handles requests it is responsible for
o can access its successor
o if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor
 Client (ChainApp)
o initiates the request to a ConcreteHandler object on the chain

Lets start by looking at the Handler abstract class i.e. Employee class.

public abstract class Employee


{
// Every employee will have a supervisor
protected Employee supervisor;

// Event mechanism to know whenever a leave has been applied


public delegate void OnLeaveApplied(Employee e, Leave l);
public event OnLeaveApplied onLeaveApplied = null;

// This will invoke events when the leave will be applied


// i.e. the actual item will be handed over to the hierarchy of
// concrete handlers.
public void LeaveApplied(Employee s, Leave leave)
{
if (onLeaveApplied != null)
{
onLeaveApplied(this, leave);
}
}

// This is the function which concrete handlers will use to take


// action, if they are able to take actions.
public abstract void ApproveLeave(Leave leave);

// getter to get the supervisor of current employee


public Employee Supervisor
Page 18 of 21
{
get
{
return supervisor;
}
set
{
supervisor = value;
}
}

// Using this we can apply for leave


public void ApplyLeave(Leave l)
{
LeaveApplied(this, l);
}
}

This Handler class is responsible for:

Keeping track of successors for this object


Implementing the eventing mechanism to notify and propagate the request event.
Initiate the request.

Now since our Handler class is ready, lets look at the ConcreteHandlers one by one. Lets start with TeamLeaderclass.

public class TeamLeader : Employee


{
// team leas can only approve upto 7 days of leave
const int MAX_LEAVES_CAN_APPROVE = 10;

// in constructor we will attach the event handler that


// will check if this employee can process or he need to
// pass on to next employee
public TeamLeader()
{
this.onLeaveApplied += new OnLeaveApplied(TeamLeader_onLeaveApplied);
}

// in this function we will check if this employee can


// process or he need to pass on to next employee
void TeamLeader_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
}
}

// If we can process lets show the output


public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "Team Leader");
}}

What this class is doing is:

Checking of this class could take the action i.e. leave applied is less than 10 days.
If this class could take the action then show the response to the user.
Page 19 of 21
If this class is not able to take the action then pass on the request to the ProjectLeader class i.e. its successor.

Now lets look at the ProjectLeader class.

class ProjectLeader : Employee


{
const int MAX_LEAVES_CAN_APPROVE = 20;

// in constructor we will attach the event handler that


// will check if this employee can process or he need to
// pass on to next employee
public ProjectLeader()
{
this.onLeaveApplied += new OnLeaveApplied(ProjectLeader_onLeaveApplied);
}

// in this function we will check if this employee can


// process or he need to pass on to next employee
void ProjectLeader_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
}
}

// If we can process lets show the output


public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "Project Leader");
}
}

What this class is doing is:

Checking of this class could take the action i.e. leave applied is less than 20 days.
If this class could take the action then show the response to the user.
If this class is not able to take the action then pass on the request to the HR class i.e. its successor.

Now lets look at the HR class.

class HR : Employee
{
const int MAX_LEAVES_CAN_APPROVE = 30;

// in constructor we will attach the event handler that


// will check if this employee can process or
// some other action is needed
public HR()
{
this.onLeaveApplied += new OnLeaveApplied(HR_onLeaveApplied);
}

// in this function we will check if this employee can


// process or some other action is needed
void HR_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
Page 20 of 21
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
else
{
// There is no one up in hierarchy so lets
// tell the user what he needs to do now
Console.WriteLine("Leave application suspended, Please contact HR");
}
}
}

// If we can process lets show the output


public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "HR");
}
}

What this class is doing is:

Checking of this class could take the action i.e. leave applied is less than 30 days.
If this class could take the action then show the response to the user.
If this class is not able to take the action then let the user know that he needs to have a manual discussion and his request has
been suspended.

The actual action item i.e. the leave is encapsulated into a class for better modularity, so before running the application lets
see how this Leave class looks like

// This is the actual Item that will be used by the concretehandlers


// to determine whther they can act upon this request or not
public class Leave
{
public Leave(Guid guid, int days)
{
leaveID = guid;
numberOfDays = days;
}

Guid leaveID;

public Guid LeaveID


{
get { return leaveID; }
set { leaveID = value; }
}
int numberOfDays;

public int NumberOfDays


{
get { return numberOfDays; }
set { numberOfDays = value; }
}
}

And finally we need the Client that will set the successor chain and initiate the request.

class Program
{
static void Main(string[] args)
{
// lets create employees
Page 21 of 21
TeamLeader tl = new TeamLeader();
ProjectLeader pl = new ProjectLeader();
HR hr = new HR();

// Now lets set the hierarchy of employees


tl.Supervisor = pl;
pl.Supervisor = hr;

// Now lets apply 5 day leave my TL


tl.ApplyLeave(new Leave(Guid.NewGuid(), 5));

// Now lets apply 15 day leave my TL


tl.ApplyLeave(new Leave(Guid.NewGuid(), 15));

// Now lets apply 25 day leave my TL


tl.ApplyLeave(new Leave(Guid.NewGuid(), 25));

// Now lets apply 35 day leave my TL


tl.ApplyLeave(new Leave(Guid.NewGuid(), 35));

Console.ReadLine();
}
}

So we can see that this Main function is creating the chain by setting the successors for each class and is initiating the request
to TeamLeader class. One request for each scenario has been made. When we run this application.

So we can see that each request get passed on and processed by the respective class based on the number of days applied
for. Before wrapping up let us look at the class diagram for our application and compare it with the original GoF diagram.

You might also like