Observer Pattern C#
Introduction
In this article, I will try to explain Observer Pattern, which is
being used intensively in software, via brief and usable example.
Definition
As a word, Observer means a person who observes (Observer Pattern).
In software world, however, it is defined as a one-to-many dependency among
objects, i.e. once the status of an object is changed; this status change is
notified to all objects dependent to that. As an example, assume that you are
observing a KeyDown event; when any key from the keyboard is pressed, the
system informs to various observers of the system which key has been pressed
(of course, by using the structure of Observer Pattern). Let us explain this
with a general example; you are a subscriber of a magazine and the magazine is
sent to you when a new issue is published. The magazine does not concern about
what you are doing with the magazine! It will just try to send new issues to
you when published. When your subscription is over, you will not get new
issues. Here you are in place of the Observer and the magazine is in Publisher.
The Observer Pattern is being used in many areas in the real life, for example
Stock Exchange Services, Whether Service and similar various areas.
Observer Pattern is using “Loose Coupling” power which is one of
the most important principles of OOPs. Actually, it is required a different
article in order to explain the Loose Coupling, but I will try to explain it
here with brief notes;
·
In case there is a loose coupling between two objects, they may
communicate and make process with each other, although they do not have much
information about each other.
·
The object in the Observer Pattern (Publisher) which shows a change
does not have much information about observers. The only thing that it knows is
that, the observers are implementing the Observer Interface.
·
Loose Coupling enables to add an observer easily when desired.
·
Loose Coupling enables to use Publisher and observer classes
repeatedly.
·
Changes made in Publisher and Observer do not affect each other.
By using this principle, we may write a code more easily, looser
and repeatedly usable.
This pattern may be used in various ways; I preferred to apply by
using Interface. The class diagram of the pattern will be in this way.
The class diagram above may be explained as follows. USDCls and
EURCls are observers and they Implement the methods of interfaces of both IGörüntü
and IGözlemci. Publisher class, however, Implements the methods of interfaces
of both independent and IObserverBs Interface. As seen in this diagram, USDCls and
EURCls are independent from each other. More importantly, these two classes are
connected with Publisher class and Loose Coupling principle. This connection is
supplied by methods Implemented from IObserverBs. In order to better understand
this diagram, let us make the application.
Application
The purpose of the application is to inform observers about the
timely change of Exchange rates. In case new rates are reflected to XML
service, our Publisher Class will notify the registered Observers about new
data. In the Application that we will make, it will be benefitted from the XML
exchange and gold service given by http://www.altinkaynak.com.
First of all create a new project (I preferred Windows Application
in the example); add a folder to the project; this folder will be our Business
Logic Folder and will accommodate required classes of entire project in it.
Let the first class to be IObserverBS Interface. This interface is
generally required to include the methods to be applied to observers: RegisterObserver,
UnsubscribeObserver and InformObservers methods will be sufficient.
void RegisterObserver(IObserver observer); void UnsubscribeObserver(IObserver observer); void InformObservers(); |
Here we see that registering and deleting methods take the observer
as a parameter. Notify method, however, does not take a parameter, because when
you call the notify method, it will notify the change to all registered
observers.
Before writing the Publisher class that Implements this Interface,
I preferred to write observer interfaces and classes.
The second Interface is IObserver interface and IObserver contains
only one method: “Update”. This method is required to be Implemented by
all Observers (I chose to apply this interface according to principle of
compression the frequently used lines and codes together, which is one of OOP -“Encapsulate
what varies”- principles). The update method takes a DataSet as a parameter (in
case the date within this DataSet changes, this will be sent to all Observers
by the Publisher class. – This dataset is being taken from Altin Kaynak
service-).
void Update(DataSet kurDS); |
The other interface that Observers will use is IDisplay interface.
Likely as in IObserver interface, this interface also has to be Implemented by
all Observers, otherwise they will not able to present the required data to the
user (IDisplay and IObserver interfaces may be joined under the same roof).
IDisplay interface do not take parameter, it returns a single string. The
content of this string is changed data.
StringBuilder Display(); |
Let us add a new class to our project; this class, as in USDCls and
EURCls, should implement both IDisplay and IObserver. In case there will be
Observer, it will need IObserver interface. In addition to this Implement
process (particular to this application) write two private strings (I wrote USDBuy
and USDSell for USDCls; EURCls also includes the same strings, but only for
Euro). This class Codes will look as below:
using System.Text; using System.Data; namespace WindowsFormsApplication1.BLL { /// <summary> /// By Muhanad YOUNIS /// 8/12/2008 Time: 4:21 PM /// Class Observing Dollar Prices /// </summary> class USDCls : IDisplay,
IObserver { private
string USDBuy; private
string USDSell; /// <summary> /// USDCls constructor, in case this class is created as
observer /// it registers itself to Observer list found in Publisher
class. /// </summary> public
USDCls(Publisher publisher) { publisher.RegisterObserver(this); } #region IDisplay Members /// <summary> /// Data to be returned from this method is used for
diplaying by the user /// </summary> /// <returns></returns> public
StringBuilder Display() { StringBuilder
SB = new StringBuilder(); SB.AppendLine("Dolar Alış =" + USDBuy); SB.AppendLine("Dolar Satış =" + USDSell); return
SB; } #endregion #region IObserver Members /// <summary> /// Renewed DataSet is informed to Observer class due to
this method /// and the required process are made. /// </summary> public
void Update(DataSet
kurDS) { USDBuy = kurDS.Tables[0].Rows[1]["ALIS"].ToString();// string is being set USDSell =
kurDS.Tables[0].Rows[1]["SATIS"].ToString();// string is being set Display();// after set process, diplay method is called. } #endregion } } |
The same processes are also valid for EURCls class.
Now it is the turn of Publisher class, the most important class of
Observer Pattern. Create a new class in the folder and Implement the IObserverBS
interface. Besides, there will be a private DataSet (there will be data comes
from DataSet) and a private ArrayList (this array list will contain the
registered observers). The Publisher class will look as below;
using System.Collections; using System.Data; using System; namespace
WindowsFormsApplication1.BLL { /// <summary> /// By Muhanad YOUNIS /// 8/12/2008 Time: 5:16
PM /// Publisher Class /// </summary> public class Publisher : IObserverBs { private ArrayList
ObserverList;// Gözlemci ArrayList private DataSet
ds;// Kaynaktan Alınan veriler #region IObserverBS Interface /// <summary> /// Publisher class
constractor, when a new publisher is generated /// a new observer list
is created. /// </summary> public Publisher() {
ObserverList = new ArrayList(); } /// <summary> /// New Observers are
registered in Observer ArryList with this method /// </summary> public void
RegisterObserver(IObserver observer) {
ObserverList.Add(observer); } /// <summary> /// delete an observer
from the arrylist /// </summary> public void
UnsubscribeObserver(IObserver observer) {
ObserverList.Remove(observer); } #endregion /// <summary> ///Notify Metodu; with
this method, information is distributed to all observers within Observer
arraylist. /// </summary> public void
InformObservers() { for (int i = 0; i
< ObserverList.Count; i++) { IObserver
o = (IObserver)ObserverList[i];
o.Update(ds);//IObserver is calling the
method implemented from interface and Oberver is being updated. } } /// <summary> /// With this method,
data is taken from the source and the dataset is being set. /// This cheks whethet
the ds in cache is equal to new coming data set /// and if it is
different calls DataChanged() method. /// </summary> public void
BindTheData() { try { ds = new DataSet();
ds.ReadXml("http://xml.altinkaynak.com.tr/altinkaynak.xml"); if (AppCache.Caching.Cache["Veriler"]
== null)// if cache
is empty(program becomes true at initial run) {
DataChanged();// when new data comes,
DataChanged methos is called AppCache.Caching.Cache.Insert("Veriler", ds);// fill Cache } else { DataSet checkDs = AppCache.Caching.Cache["Veriler"]
as DataSet; for (int i = 1; i
< ds.Tables[0].Rows.Count; i++) {
/// Checks
whether the rates are changed or not
/// the
change of a rate is enough for data set to be resent to observers.
/// RETURN;
used to finished the loop.
if (ds.Tables[0].Rows[i][1].ToString()
!= checkDs.Tables[0].Rows[i][1].ToString() ||
ds.Tables[0].Rows[i][2].ToString() !=
checkDs.Tables[0].Rows[i][2].ToString())
{
DataChanged();// when new data comes,
DataChanged methos is called
AppCache.Caching.Cache.Insert("Veriler", ds);
return;//finish
the loop
} } } } catch (Exception) { throw; } } /// <summary> /// Data changed, notify
Observers. /// </summary> public void
DataChanged() {
InformObservers(); } } } |
Thus, I explained all classes
included in Observer Pattern application. Now it is the time to call these
classes over Form.
Place two Labels in Form Design and add a Timer to the Form. Adjust the
Interval value of the Timer as 30000 ms (30 seconds – “data source will be
checked once in 30 seconds”). Create a new method, StartPublisher(), in the CS
of the Form. By using this method, a new Instance of Publisher class will be
generated and at the same time, our desired Observers (USDCls and EURCls) will
be registered to the new Publisher.
/// <summary> /// Generation of new
Publisher and Observer registering method. /// </summary> private void
Startpublisher() { Publisher publisher = new
Publisher();//
New Publisher Class was generated USDCls usd = new
USDCls(publisher);//
Observer was registered to Publisher EURCls eur = new
EURCls(publisher);//
Observer was registered to Publisher
publisher.BindTheData();// Data is taken
from the source of Publisher and given to Observers if (usd.Display().Length != 29 &&
eur.Display().Length != 27)// the numbers are
related with the xml data !! {
label1.Text = usd.Display().ToString();//
Data came from Observers are being diplayed
label2.Text = eur.Display().ToString();//
Data came from Observers are being diplayed } else {
label1.Text = label1.Text;
label2.Text = label2.Text; } } |
StartPublisher()
method is required to be called from two places of the Form: Load and Timer_Tick
Event of Form. Timer will call this method once in 30 seconds and enables data
to be updated. (Do not forget to initiate Timer in Form Load Event).
private void Form1_Load(object sender, EventArgs e) {
Startpublisher();// publisher is generated
timer1.Start();// timer is run } /// <summary> /// Generation of new
Publisher and Observer registering method. /// </summary> private void
Startpublisher() { Publisher publisher = new
Publisher();//
New Publisher Class was generated USDCls usd = new
USDCls(publisher);//
Observer was registered to Publisher EURCls eur = new
EURCls(publisher);//
Observer was registered to Publisher
publisher.BindTheData();// Data is taken
from the source of Publisher and given to Observers if (usd.Display().Length != 29 &&
eur.Display().Length != 27)// the numbers are related
with the xml data !! {
label1.Text = usd.Display().ToString();//
Data came from Observers are being diplayed
label2.Text = eur.Display().ToString();//
Data came from Observers are being diplayed } else {
label1.Text = label1.Text;
label2.Text = label2.Text; } } |
When the
Application is first run, the data will be taken from XML service and given to
observers. Observers will reflect the data to the user as below;
This process will
be repeated once in 30 second. Data changes!
If you are lucky,
rates change and you can see that change after 30 seconds (when I wrote this
article, I has to wait more than 2 hours to see the rates change J).
If you want to add
a different Observer, the only thing that you need to do is to implement class
from IObserver and IDisplay interface and register it on Publisher Class. With
this way you will be created a new observer.
Note: I recommend
you to debug the apllication step by step.
Conclusion
In this article, I tried to exlplain
Observer Pattern with a simple application. Together with the Pattern, I
mentioned about Loose Coupling and Encapsulate what Varies principles. You can
progress the application differently and in more detail, it is up to you.
Note: You can download here the code made in the article. The speed of the application
in the first opening depends on your Internet speed (How fast the data comes
from XML service, it opens that fast). The application was made with VS2008 and
Net 3.5 SP1.
Hope This Helps
Ref
http://www.dofactory.com/Patterns/PatternObserver.aspx
http://www.javaworld.com/javaworld/javaqa/2001-05/04-qa-0525-observer.html
http://wiki.asp.net/page.aspx/96/architecture/