GoF book says that "Observer pattern should defina a one-to-many dependency between objects so that when one object changes state, all its dependenst are notified and updated automatically". Subscribing to RSS feeds is a nice analogy. You subscribe to RSS feeds to show interest and you become an observer who demand for notification and RSS feeds become the subject and are responsable for providing information to all subscribers. I think this bit of information describing the pattern is enough, now lets see how we implement observer pattern.
Lets assume that we are developing a car controller software. One of the requirements is "Provide visual warning to the driver if seat belts are not locked". To meet the requirement we design
- SeatBelt class which is the subject. This class should notify interested objects about the state changes
- SeatBeltMonitor class is our observer. This class monitors for status change messages provided by the SeatBelt and displays messages
Implementing Observer Pattern in conventional way
Download GoF_Patterns_Observer1.rar (20,36 kb)
It is suggested to specify an interface or abstract classes when dealing with patterns so that the objects we create will adhere to some predefined contract. So we start our implementation by specifying two interfaces ISubject and IObserver
001 public interface ISubject
002 {
003 void RegisterObserver(IObserver observer);
004 void RemoveObserver(IObserver observer);
005 }
006
007 public interface IObserver
008 {
009 void Update(bool locked);
010 }
As I've mentioned above our SeatBelt class is the subject (source) of the state change and should implement the contract so that interested parties shall define their interest to the state changes or stop receiving state change notification. Here is our SeatBelt code
001 public class SeatBelt:ISubject
002 {
003 private IList<IObserver> _observers = new List<IObserver>();
004
005 private bool _locked = false;
006 public bool Locked
007 {
008 get { return _locked; }
009 set { _locked = value; UpdateObservers(); }
010 }
011
012
013 private void UpdateObservers()
014 {
015 foreach (IObserver observer in _observers)
016 {
017 try
018 {
019 observer.Update(_locked);
020 }
021 catch (Exception ex)
022 {
023 Console.WriteLine("Can not update observer. Error:" + ex.Message);
024 }
025 }
026 }
027
028 #region IObserver Members
029
030 public void RegisterObserver(IObserver observer)
031 {
032 if (observer == null || _observers.Contains(observer))
033 return;
034
035 _observers.Add(observer);
036 }
037
038 public void RemoveObserver(IObserver observer)
039 {
040 if (observer == null || !_observers.Contains(observer))
041 return;
042
043 _observers.Remove(observer);
044 }
045
046 #endregion
047 }
When value of the Locked property changes all registered observers will be notified. Our SeatBeltMonitor class simply responds to the change with a message. Here is our observer code
001 public class SeatBeltMonitor:IObserver
002 {
003 #region IObserver Members
004
005 public void Update(bool locked)
006 {
007 if (!locked)
008 Console.WriteLine("Please lock your seat belt!");
009 else
010 Console.WriteLine("Seat belt OK.");
011 }
012 #endregion
013 }
001 static void Main(string[] args)
002 {
003 SeatBelt seatBelt = new SeatBelt();
004 SeatBeltMonitor monitor = new SeatBeltMonitor();
005
006 seatBelt.RegisterObserver(monitor);
007 // Prints OK.
008 seatBelt.Locked = true;
009
010 //Prints warning
011 seatBelt.Locked = false;
012
013 seatBelt.RemoveObserver(monitor);
014 // Nothing will be printed
015 seatBelt.Locked = true;
016
017
018 Console.Read();
019 }
Implementing Observer Pattern with .NET Events
Download GoF_Patterns_Observer2.rar (20,72 kb)
.NET and C# provides a very flexible implementation of Observer Pattern through events and delegates.Here is the code for the alternative implementation. Some differences are
- We do not need to define contracts through ISubject and IObserver interfaces
- We specify the contract through delegate definitions (SeatBeltStatusChangedDelegate)
- We use custom classes as convention to pass status data from subject to the observers (SeatBeltEventArgs class)
- We register method pointers through events of the subject rather than registering our observer objects to our subject. In this case method signature is used as contract
- We use overloaded + and - operators for registration
001 public class SeatBeltEventArgs : EventArgs
002 {
003 public bool Locked { get; set; }
004 }
005
006 public class SeatBelt
007 {
008 private bool _locked = false;
009 public bool Locked
010 {
011 get { return _locked; }
012 set { _locked = value; FireStatusChangeEvent(); }
013 }
014
015 private SeatBeltStatusChangedDelegate _statusChanged;
016 public event SeatBeltStatusChangedDelegate StatusChanged
017 {
018 add { _statusChanged += value; }
019 remove { _statusChanged -= value; }
020 }
021
022
023 private void FireStatusChangeEvent()
024 {
025 if (_statusChanged == null)
026 return;
027
028 Delegate[] invokeList = _statusChanged.GetInvocationList();
029 foreach (SeatBeltStatusChangedDelegate d in invokeList)
030 {
031 SeatBeltEventArgs args = new SeatBeltEventArgs();
032 args.Locked = _locked;
033 try
034 {
035 d.Invoke(this, args);
036 }
037 catch (Exception ex)
038 {
039 Console.WriteLine("Can not update observer. Error:" + ex.Message);
040 }
041 }
042 }
043 }
044
045 public delegate void SeatBeltStatusChangedDelegate(object sender, SeatBeltEventArgs args);
046
047
048
001 public class SeatBeltMonitor
002 {
003 public void RespondToSeatBeltStatusChange( object sender, SeatBeltEventArgs args)
004 {
005 if (!args.Locked)
006 Console.WriteLine("Please lock your seat belt!");
007 else
008 Console.WriteLine("Seat belt OK.");
009 }
010 }
001 static void Main(string[] args)
002 {
003 SeatBelt seatBelt = new SeatBelt();
004 SeatBeltMonitor monitor = new SeatBeltMonitor();
005
006 seatBelt.StatusChanged += new SeatBeltStatusChangedDelegate(monitor.RespondToSeatBeltStatusChange);
007 // Prints OK.
008 seatBelt.Locked = true;
009
010 //Prints warning
011 seatBelt.Locked = false;
012
013 seatBelt.StatusChanged -= new SeatBeltStatusChangedDelegate(monitor.RespondToSeatBeltStatusChange);
014 // Nothing will be printed
015 seatBelt.Locked = true;
016
017
018 Console.Read();
019 }
020
021
022
Currently rated 4.0 by 1 people
- Currently 4/5 Stars.
- 1
- 2
- 3
- 4
- 5