Wednesday, September 14, 2011

The Observer Pattern

With this post I opening my new posts line of Design Patterns.
And First one, I think, important one - Observer Pattern.
I really hope I'll possible explain pattern in easy and understandable way.
Enjoy :-)

Why to use:

The need to maintain consistency between related objects without making
classes tightly coupled.


When to use:


Use the Observer pattern in any of the following situations:
- When an abstraction has two aspects, one dependent on the other.
   Encapsulating these aspects in separate objects lets you vary and reuse
   them independently.
- When a change to one object requires changing others
- When an object should be able to notify other objects without making
   assumptions about those objects





Participants:
Subject
  • Keeps track of its observers
  • Provides an interface for attaching and detaching Observer objects

Observer

  • Defines an interface for update notification

ConcreteSubject

  • The object being observed
  • Stores state of interest to ConcreteObserver objects
  • Sends a notification to its observers when its state changes

ConcreteObserver

  • The observing object
  • Stores state that should stay consistent with the subject's
  • Implements the Observer update interface to keep its state consistent with the subject's
Benefits
Minimal coupling between the Subject and the Observer
  • Can reuse subjects without reusing their observers and vice versa
  • Observers can be added without modifying the subject
  • All subject knows is its list of observers
  • Subject does not need to know the concrete class of an observer, just that each observer implements the update interface
  • Subject and observer can belong to different abstraction layers
Support for event broadcasting
  • Subject sends notification to all subscribed observers
  • Observers can be added/removed at any time

Consequences
Liabilities
  • Possible cascading of notifications
  • Observers are not necessarily aware of each other and must be careful about triggering updates
  • Simple update interface requires observers to deduce changed item

Implementation Issues
How does the subject keep track of its observers?
  • Array, linked list
What if an observer wants to observe more than one subject?
  • Have the subject tell the observer who it is via the update interface
Who triggers the update?
  • The subject whenever its state changes
  • The observers after they cause one or more state changes
  • Some third party object(s)
Make sure the subject updates its state before sending out notifications
How much info about the change should the subject send to the observers?
  • Push Model - Lots
  • Pull Model - Very Little

Can the observers subscribe to specific events of interest?
  • If so, it's publish-subscribe
Can an observer also be a subject?
  • Yes!
What if an observer wants to be notified only after several subjects have
changed state?
  • Use an intermediary object which acts as a mediator
  • Subjects send notifications to the mediator object which performs any necessary processing before notifying the observers.
Known Uses
Smalltalk Model/View/Controller user interface framework
  • Model = Subject
  • View = Observer
  • Controller is whatever object changes the state of the subject
Related Patterns
  • Mediator
              › To encapsulate complex update semantics.



And now, some thing you are waiting for....code sample :-)

Observable Class:

public Observable()
 Construct an Observable with zero Observers
public synchronized void addObserver(Observer o)
 Adds an observer to the set of observers of this object
public synchronized void deleteObserver(Observer o)
 Deletes an observer from the set of observers of this object
protected synchronized void setChanged()
 Indicates that this object has changed
protected synchronized void clearChanged()
 Indicates that this object has no longer changed, or that it has already
 notified all of its observers of its most recent change.  This method is called
 automatically by notifyObservers().

public synchronized boolean hasChanged()
 Tests if this object has changed.  Returns true if setChanged() has been
 called more recently than clearChanged() on this object; false otherwise.
public void notifyObservers(Object arg)
 If this object has changed, as indicated by the hasChanged() method, then
 notify all of its observers and then call the clearChanged() method to
 indicate that this object has no longer changed.  Each observer has its
 update() method called with two arguments: this observable object and the
 arg argument. The arg argument can be used to indicate which attribute of
 the observable object has changed.
public void notifyObservers()
 Same as above, but the arg argument is set to null.  That is, the observer is
given no indication what attribute of the observable object has changed.

Observer Interface

public abstract void update(Observable o, Object arg)
 This method is called whenever the observed object is changed. An
 application calls an observable object's notifyObservers method to have all
 the object's observers notified of the change.
 Parameters:
   › o - the observable object
   › arg - an argument passed to the notifyObservers method

-------------------------------------------------------------------------------------------------------


/**
 * A subject to observe!
 */
public class ConcreteSubject extends Observable
{
  private String name;
  private float price;

  public ConcreteSubject(String name, float price)
  {
    this.name = name;
    this.price = price;
    System.out.println("ConcreteSubject created: " + name + " at "
      + price);
  }

  public String getName() {return name;}
  public float getPrice() {return price;}

  public void setName(String name)
  {
    this.name = name;
    setChanged();
    notifyObservers(name);
   }

  public void setPrice(float price)
  {
    this.price = price;
    setChanged();
    notifyObservers(new Float(price));
  }
}

----------------------------------------------------------------------------------------------------


An observer of name changes.
public class NameObserver implements Observer
{
  private String name;
  public NameObserver()
 {
    name = null;
    System.out.println("NameObserver created: Name is " + name);
  }
  public void update(Observable obj, Object arg)
  {
    if (arg instanceof String)
    {
      name = (String)arg;
      System.out.println("NameObserver: Name changed to " + name);
    }
    else
    {
      System.out.println("NameObserver: Some other change to subject!");
     }
   }
}

// An observer of price changes.
public class PriceObserver implements Observer
{
  private float price;
  public PriceObserver()
  {
    price = 0;
    System.out.println("PriceObserver created: Price is " + price);
  }
  public void update(Observable obj, Object arg)
  {
    if (arg instanceof Float)
    {
      price = ((Float)arg).floatValue();
      System.out.println("PriceObserver: Price changed to " + price);
    }
    else
    {
      System.out.println(”PriceObserver: Some other change to subject!");
    }
  }
}

----------------------------------------------------------------------------------------------------

// Test program for ConcreteSubject, NameObserver and PriceObserver
public class TestObservers
{
    public static void main(String args[])
    {
        // Create the Subject and Observers.
        ConcreteSubject s = new ConcreteSubject("Corn Pops", 1.29f);
        NameObserver nameObs = new NameObserver();
        PriceObserver priceObs = new PriceObserver();
        // Add those Observers!
        s.addObserver(nameObs);
        s.addObserver(priceObs);
        // Make changes to the Subject.
        s.setName("Frosted Flakes");
        s.setPrice(4.57f);
        s.setPrice(9.22f);
        s.setName("Sugar Crispies");
    }
}

Test program output
    ConcreteSubject created: Corn Pops at 1.29
    NameObserver created: Name is null
    PriceObserver created: Price is 0.0
    PriceObserver: Some other change to subject!
    NameObserver: Name changed to Frosted Flakes
    PriceObserver: Price changed to 4.57
    NameObserver: Some other change to subject!
    PriceObserver: Price changed to 9.22
    NameObserver: Some other change to subject!
    PriceObserver: Some other change to subject!
    NameObserver: Name changed to Sugar Crispies



No comments:

Post a Comment