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.

class

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/

            http://www.research.ibm.com/designpatterns/example.htm

7 Comments

Comments have been disabled for this content.