Observer Design Pattern


DESIGN PATTERS - Behavioral Patterns - Observer Pattern (with and without delegate/events) - Source codes for both scenarios are attached (included)


DESIGN PATTERS - Behavioral Patterns - Observer Pattern

## Environment: Ms Windows XP Prof Ver 2002 (SP 2) & Visual Studio 2008 ##

INTRODUCTION

Observer pattern is useful in the scenarios where the state of object is changing frequently and those changes need to communicate to one or more observers automatically. In most standard scenario, it has 3 participants:

a.) Subject - Object or an entity class that is being changed and those changes need to be notified to Observers.
b.) IObserver - An Interface that mentions, how the Observers will be notified.
c.) Observers – It implement IObserver's method which are fired during change of Subject Data State.

SCENARIO WHERE OBSERVER PATTERN CAN BE USED

Observer pattern may be applicable in the "Stock Market" Scenario. Where the subject may be stock information along some ever-changing data like, Open price, close price, share price etc. And observers may be the investors, client, subscriber or a channel and that needs to be notified whenever there is a change in data of registered companies.

Observer Pattern is effectively used in MVC - applications where client applications are automatically communicated about the changes in the data collection.

SAMPLE 1 - COMPLETE PROTOTYPE FOR OBSERVER PATTERN (Without using Delegated & Events)

UML - Representation...

Refer : UML - Representation of Observer (Without Delegate/Events)

(This attachment is in DOC format so that you can download and modify it for your need)

STEPS - Implementation...

Here I am taking a scenario where Subject has a "company information" and multiple observers (clients) have registered to subject and get notified whenever there is a change in the company's data.

a.) Create a Business Entity class which we are planning to transfer between Observer and Subject.
b.) Create an Abstract Class for Observer with Update() without its implementation.
c.) Create an Abstract Class for Subject with implementation for Attach(), Detach() and Notify() methods.
d.) Derive Observer from Abstract Observer class & implement Update() method to provide custom logic on the received data.
e.) Derive Subject from Abstract Subject class.
f.) How to use this implementation in a test client (see below)...
f.1) Create instance of Subject class and pass a Business entity to it as an initial data.
f.2) Create multiple instances of Observer class.
f.3) Register these Observer instances to Subject object's Attach() for Auto notification.
f.4) Modify some properties of Business Entity which is stored in Subject object. This is termed as "change in Subject DataState".
f.5) Invoke Subject object's Notify() method


Start with a fresh Console application > Name it Observer1 > Create following individual classes in it.

a.) Business Entity class


public class CompanyEntity
{
private int companyCode;
private string companyName;
private double shareRate;

public CompanyEntity() { }

public CompanyEntity(int companyCode, string companyName, double shareRate)
{
this.companyCode = companyCode;
this.companyName = companyName;
this.shareRate = shareRate;
}

public int CompanyCode
{
get { return companyCode; }
set { companyCode = value; }
}

public string CompanyName
{
get { return companyName; }
set { companyName = value; }
}

public double ShareRate
{
get { return shareRate; }
set { shareRate = value; }
}
}


b.) Observer Abstract Class

public abstract class AbstractObserver
{
public abstract void Update(DateTime dt);
}


c.) Subject Abstract Class

public abstract class AbstractSubject
{
private ObserverList list = new List();

public void Attach(AbstractObserver o)
{
list.Add(o);
}

public void Detach(AbstractObserver o)
{
list.Remove(o);
}

public void NotifyToObserver()
{
foreach (AbstractObserver o in list)
{
System.Threading.Thread.Sleep(2000);
o.Update(DateTime.Now);
}
}
}


d.) Observer Concrete Class


public class ConcreteObserver : AbstractObserver
{
private string uid = "";
private CompanyEntity ce = new CompanyEntity();
private ConcreteSubject cs = new ConcreteSubject();

public ConcreteObserver(ConcreteSubject cs, string uid)
{
this.uid = uid;
this.cs = cs;
}

public override void Update(DateTime dt)
{
this.ce = cs.Ce;
Console.Write(" {0} {1} {2} {3} {4}\n", uid.ToString().PadRight(8, ' '),
ce.CompanyCode.ToString().PadRight(9, ' '), ce.CompanyName.PadRight(15, ' '),
ce.ShareRate.ToString().PadRight(10, ' '), dt.ToString());
}

public ConcreteSubject Cs
{
get { return cs; }
set { cs = value; }
}
}


e.) Subject Concrete Class


public class ConcreteSubject : AbstractSubject
{
private CompanyEntity ce;

public ConcreteSubject()
{
}

public ConcreteSubject(CompanyEntity ce)
{
this.ce = ce;
}

public double NewRate
{
get { return ce.ShareRate; }
set { ce.ShareRate = value; }
}

public CompanyEntity Ce
{
get { return ce; }
set { ce = value; }
}
}


f.) Test Client


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

// create Company data and send to Subject
CompanyEntity ce = new CompanyEntity(100, "Company1", 15.5);
ConcreteSubject cs = new ConcreteSubject(ce);

// create observers along with their filter lists
ConcreteObserver co1 = new ConcreteObserver(cs, "Code 1");
ConcreteObserver co2 = new ConcreteObserver(cs, "Code 2");
ConcreteObserver co3 = new ConcreteObserver(cs, "Code 3");

// register these Observers in the Subject
cs.Attach(co1);
cs.Attach(co2);
cs.Attach(co3);

Console.WriteLine("Call# ClientID CompanyID Company Name Share Rate Notified at ");
Console.WriteLine("----- -------- --------- --------------- ---------- ----------------");

for (int i = 1; i <= 3; i++)
{
// Make changes in the Rate and call Notify of subject
cs.NewRate = 15.8;
Console.WriteLine("Notification {0}...\n",i);
cs.Notify();
Console.WriteLine("----- -------- --------- --------------- ---------- ------------");
}

Console.WriteLine("All Notified");
Console.ReadKey();
}
}


Compile and test it

Output

Refer : OUTPUT - (Observer without delegate/event)

SAMPLE 2 - COMPLETE PROTOTYPE FOR OBSERVER PATTERN (With using Delegated & Events)

UML - Representation...

Refer : UML - Representation of Observer (With Delegate/Events)

(This attachment is in DOC format so that you can download and modify it for your need)

STEPS - Implementation...

Here I am taking share market example where "multiple companies" are listed. And some observers (clients) register themselves with their specific companies.

a.) Create a Business Entity class which we are planning to transfer between Observer and Subject.
b.) Create an ObserverData class which would hold Observer Code and its filter criteria.
c.) Derive Custom EventArgs class from EventArgs to hold Collection of Business Entity.
d.) Create an Interface for Observer with Update() method.
e.) Create an Interface for Subject with AttachToEvent().
f.) Implement IObserver's Update() method in Concrete Observer class to provide custom logic on the received data.
g.) Implement ISubject's AttachToEvent() method in Concrete Subject class and also write some method to Automatize Event Fire.
h.) How to use this implementation in a test client (see below)...
h.1) Create instance of Subject class
h.2) Create multiple instances of Observer class with Subject object and specific filtering data (may be a list or criteria or no filter).
h.3) Register these Observer instances to Subject object's AttachToEvent method for Auto notification.
h.4) Now we need some initial data to Subject object. For this we can create a collection of Business Entity & store in Subject object.
h.5) Once everything is in place, we can invoke the Subject's method for automation of process.

Start with a fresh Console application > Name it Observer2 > Create following individual classes in it

a.) Business Entity class


public class CompanyEntity
{
private int companyCode;
private string companyName;
private double shareRate;

public CompanyEntity( ) { }
public CompanyEntity(int companyCode, string companyName, double shareRate)
{
this.companyCode = companyCode;
this.companyName = companyName;
this.shareRate = shareRate;
}

public int CompanyCode
{
get { return companyCode; }
set { companyCode = value; }
}

public string CompanyName
{
get { return companyName; }
set { companyName = value; }
}

public double ShareRate
{
get { return shareRate; }
set { shareRate = value; }
}
}


b.) ObserverData class


public class ObserverData
{
private string observerCode;
private ArrayList dataFilter;
public ObserverData() { }

public ObserverData(string observerCode,
ArrayList dataFilter)
{
this.observerCode = observerCode;
this.dataFilter = dataFilter;
}

public string ObserverCode
{
get { return observerCode; }
set { observerCode = value; }
}

public ArrayList DataFilter
{
get { return dataFilter; }
set { dataFilter = value; }
}
}


c.) MyEventArgs class (Custom Event Argument class)


public class MyEventArgs : EventArgs
{
public CompanyEntity Smc { get; set; }
public ArrayList CompanyEntityCollection { get; set; }
}


d.) Interface for Observer functionalities


public interface ISubject
{
void AttachToEvent(IObserver concreteObserver);
}


e.) Interface for Subject functionalities


public interface ISubject
{
void AttachToEvent(IObserver concreteObserver);
}


c.) Observer's Concrete Class


public class Observer : IObserver
{
private string uid = "";
private CompanyEntity smc = new CompanyEntity();
private Subject cs = new Subject();

private ArrayList companyEntityCollection = null;
private ObserverData observerData = null;

public Observer(Subject cs, string uid)
{
this.uid = uid;
this.cs = cs;
}

public Observer(Subject cs, ObserverData observerData)
{
this.observerData = observerData;
this.cs = cs;
}

public void Update(object sender, MyEventArgs mea)
{
this.CompanyEntityCollection = mea.CompanyEntityCollection;
CompanyEntity ce = null;
foreach (object obj in CompanyEntityCollection)
{
ce = (CompanyEntity)obj;

if (observerData.DataFilter.IndexOf(ce.CompanyCode) != -1)
{
Console.WriteLine("{0} {1} {2} {3}",
observerData.ObserverCode.ToString().PadRight(7, ' '),
ce.CompanyName.PadRight(15, ' '),
ce.OpenValue.ToString("##.00").PadRight(10),
ce.ClosingValue.ToString("##.00").PadRight(10,' '));
}
}
}

public Subject Cs
{
get { return cs; }
set { cs = value; }
}

public ArrayList CompanyEntityCollection
{
get { return companyEntityCollection; }
set { companyEntityCollection = value; }
}

public ObserverData ObserverData
{
get { return observerData; }
set { observerData = value; }
}
}


d.) Subject's Concrete Class


public class Subject : ISubject
{
private ArrayList companyEntityCollection = null;

public delegate void StatusChangedDelegate(object sender, MyEventArgs mea);
public event StatusChangedDelegate StateChangeEvent;

public ArrayList CompanyEntityCollection
{
get { return companyEntityCollection; }
set { companyEntityCollection = value; }
}

public void RunStockmarket(int start, int end)
{
for (int i = start; i <= end; i++)
{
EventArgs e = new EventArgs();
MyEventArgs mea = new MyEventArgs();
mea.CompanyEntityCollection = this.companyEntityCollection;

// envoke event
if (StateChangeEvent != null)
{
StateChangeEvent(this, mea);
Console.WriteLine("------- --------------- ---------- ----------");
System.Threading.Thread.Sleep(1000);
}
}
}

public void AttachToEvent(IObserver concreteObserver)
{
this.StateChangeEvent += new StatusChangedDelegate(concreteObserver.Update);
}
}


f.) Test Client


public class Program
{
static void Main(string[] args)
{
Subject cs = new Subject();

// create a filter list for individual obserevers
ObserverData od1 = new ObserverData("View1", new ArrayList() { 100, 101 });
ObserverData od2 = new ObserverData("View2", new ArrayList() { 101, 102 });
ObserverData od3 = new ObserverData("View3", new ArrayList() { 102, 100 });

// create observers along with their filter lists
Observer co1 = new Observer(cs, od1);
Observer co2 = new Observer(cs, od2);
Observer co3 = new Observer(cs, od3);

// register these Observers in the Subject
cs.AttachToEvent(co1);
cs.AttachToEvent(co2);
cs.AttachToEvent(co3);

// create a dummy of company and add to Subject
CompanyEntity smc1 = new CompanyEntity(100, "Company1", 45);
CompanyEntity smc2 = new CompanyEntity(101, "Company2", 25);
CompanyEntity smc3 = new CompanyEntity(102, "Company3", 15);

// create a collection of Companies data and add to Subject
ArrayList companyEntityCollection = new ArrayList();
companyEntityCollection.Add(smc1);
companyEntityCollection.Add(smc2);
companyEntityCollection.Add(smc3);
cs.CompanyEntityCollection = companyEntityCollection;

// Auto-notify (Start the business)

Console.WriteLine("Observer with Delegate and Event\n");
Console.WriteLine("Cust ID Company name Share Rate ");
Console.WriteLine("------- --------------- ---------- ");
cs.RunStockmarket(1, 2);
Console.WriteLine("All Notified");
Console.ReadKey();
}
}


Compile and test it

Output

Refer : OUTPUT - (Observer with delegate/event)

I hope, in this article, I am able to give some insights to Observer Pattern. (If any query, please write)

Regards.


Attachments

  • Source Code - Observer - Without Delegate/Event (39860-281211-PR-Without-event-and-delegate.zip)
  • Source Code - Observer - With Delegate/Event (39860-281212-PR-With-event-and-delegate-and-MultiCompany.zip)
  • Comments

    Author: Nisar29 Jul 2010 Member Level: Gold   Points : 0

    good work man... keep posting such kind of resources.

    Author: Ahsan Murshed12 Oct 2010 Member Level: Bronze   Points : 0

    Very helpful to understand !! nice one keep it up!!



  • Do not include your name, "with regards" etc in the comment. Write detailed comment, relevant to the topic.
  • No HTML formatting and links to other web sites are allowed.
  • This is a strictly moderated site. Absolutely no spam allowed.
  • Name:
    Email: