Observer design pattern (also known as Dependants and Publish-subscribe) is a behavioral pattern. Behavioral patterns are those patterns that are most specifically concerned with communication between objects.

Intent of Observer pattern is to define one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Situation of using Observer pattern

  1. When an abstraction has two aspects, one dependent on the other.
  2. When a change to one object requires changing others, and you don’t know how many objects need to be changed
  3. When an object should be able to notify other objects without making assumptions about who these objects are.

Solution

Observer pattern can be implemented using following objects.

Subject knows its observers. Any number of Observer objects may observe a subject. It provides an interface for attaching and detaching Observer objects.

Observer defines an updating interface for objects that should be notified of changes in a subject.

ConcreteSubject stores state of interest to ConcreteObserver objects. It sends a notification to its observers when its state changes.

ConcreteObserver maintains a reference to a ConcreteSubject object. It stores state that should stay consistent with the subject’s. It implements the Observer updating interface to keep its state consistent with the subject’s.

Spreadsheet Example:

Observer Implementation using Java

Java API implements a framework for this pattern. Java’s observer interface is the Observer abstract class in the pattern. Java’s Observable class is the Subject abstract class in the pattern

Class java.util.Observable

Observable object may have any number of Observers. Whenever the Observable instance changes, it notifies all of its observers. Notification is done by calling the update() method on all observers. Following are the methods of Observable class:

  • addObserver(Observer) adds an observer to the observer list.
  • clearChanged() clears an observable change.
  • countObservers() counts the number of observers.
  • deleteObserver(Observer) deletes an observer from the observer list.
  • deleteObservers() deletes observers from the observer list.
  • hasChanged() returns a true boolean if an observable change has occurred.
  • notifyObservers() notifies all observers if an observable change occurs.
  • notifyObservers(Object) notifies all observers of the specified observable change which occurred.
  • setChanged() Sets a flag to note an observable change.

Interface java.util.Observer

When implemented, this interface allows all classes to be observable by instances of class Observer. Observer interface has only one important method that is worth mentioning here.

  • update() called when observers in the observable list need to be updated

A brief description of Observer example

Following is a complete Java example implementation of Observer pattern. Following is also brief description of each of the classes used in this example.

Counter: A Counter can increase or decrease by one. Each time a counter changes value, it notifies its observers of the type of change.

IncreaseDectector: IncreaseDetector is an observer that observes counters. IncreaseDetector counts the number of times one of its observables increases. It notifies its observers when it changes.

CounterButton: Abstract class for changing a counter each time the button is pressed

IncreaseButton: A button that increases a counter each time the button is pressed

DecreaseButton: A button that decreases a counter each time the button is pressed

CounterView: A parent window view that observes a counter. Does nothing with counter. Used as parent for other views.

CounterTextView: A window for displaying the value of a counter in ASCII.

ButtonController: A window for changing the value of a counter using IncreaseButton and DecreaseButton.

RectangleView: Draws a colored rectangle that depends on two counters. One counter is the width of the rectangle, the other counter is the height of the rectangle. The color of rectangle varies with its shape.

Example Code

Counter class:

/** A counter can increase/decrease by 1. Each time a counter changes value, it notifies its observers of the type of change. */
class Counter extends Observable {
   public static final String INCREASE = "increase";
   public static final String DECREASE = "decrease";
   private int count = 0;    private String label;
   public Counter( String label ) { this.label = label; }
   public String label() { return label; }
   public int value() { return count; }
   public String toString() { return String.valueOf( count );}
   public void increase() {
      count++; setChanged();
      notifyObservers( INCREASE );
   }
   public void decrease() {
      count--;
      setChanged();
      notifyObservers( DECREASE );
   }
}

IncreaseDetector class:

/** * IncreaseDetector is an observer that observes counters.
     *IncreaseDetector counts the number of times one of its
     * observables increases. */

class IncreaseDetector extends Counter implements Observer {
   public IncreaseDetector( String label ) {
      super( label );
   }

   public void update( Observable whatChanged, Object message) {
      if ( message.equals( Counter.INCREASE) ) increase();
   }
}

CounterButton class:

/** * An abstract class for changing a counter each time the button is pressed */
abstract class CounterButton extends Button {
   protected Counter count;
   public CounterButton( String buttonName, Counter count ) {
      super( buttonName );
      this.count = count;
   }

   public boolean action( Event processNow, Object argument ) {
      changeCounter();
      return true;
   }

   abstract protected void changeCounter();
}

IncreaseButton class:

/** * A button that increases a counter each time it is pressed */
class IncreaseButton extends CounterButton {
   public IncreaseButton( Counter count ) {
      super( "Increase", count );
   }

   protected void changeCounter() {
      count.increase();
   }
}

DecreaseButton class:

/** * A button that decreases a counter each time it is pressed */

class DecreaseButton extends CounterButton {
   public DecreaseButton( Counter count ) {
      super( "Decrease", count );
   }

   protected void changeCounter() {
      count.decrease();
   }
}

CounterView class:

/** * A parent window view that observes a counter */
class CounterView extends Frame implements Observer {
   public CounterView( String label, int x, int y, int width, int height ) {
      setTitle( label );
      reshape(x, y, width, height );
      setLayout( new FlowLayout() );
   }

/** * Redraw the window when an observed counter changes */
   public void update(Observable counter, Object argument) {
      repaint();
   }
}

CounterTextView class:

/** * A window for displaying the value of a counter in ascii */
class CounterTextView extends CounterView {
   Counter model;
   public CounterTextView( Counter model, String label, int x, int y, int width, int height ) {
      super( label, x, y, width, height );
      this.model = model;
      model.addObserver( this );
      show();
   }
   public void paint( Graphics display ) {
      int y = bounds().height - 20;
      int x = 20;
      display.drawString( "The value of " + model.label() + " is " + model , x, y );
   }
}

ButtonController class:

/** * A window for changing the value of a counter */
class ButtonController extends CounterView {
   public ButtonController( Counter model, int x, int y, int width, int height ) {
      super( model.label(), x, y, width, height );
      model.addObserver( this );
      // show the value of the counter
      new CounterTextView( model, "Value of " + model.label(), x + width + 5,y, 150, 50);
      // buttons to change counter
      add( new IncreaseButton( model ));
      add( new DecreaseButton( model ));
      show();
   }
}

RectangleView class:

/** * Draws a colored rectangle that depends on two counters. One counter is the width, the other counter is the height of the rectangle. The color of rectangle varies with its shape */
class RectangleView extends CounterView {
   Counter width;
   Counter height;

   public RectangleView( Counter rectWidth, Counter rectHeight, int x, int y ) {
      super( "Rectangle", x, y, 150, 150 );
      height = rectHeight;
      width = rectWidth;
      rectWidth.addObserver( this );
      rectHeight.addObserver( this );
      show();
   }
      public void paint( Graphics display ) {
      int x = 10;
      int y = 10;
      // Magnify value by 5 to get a bigger visual effect
      int height = 5 * this.height.value();
      int width = 5 * this.width.value();
      // Determine color. Colors chosen for fun.
     // The factor of 3 is just to magnify effect of change
     if (( width >= 0 ) && ( height >= 0 ))
         display.setColor( new Color( 3*width, 3*height, width + height) );
      else if (( width < 0 ) && ( height >= 0 ))
              display.setColor( Color.pink );
      else if (( width >= 0 ) && ( height < 0 ))
              display.setColor( Color.orange );
      else if (( width < 0 ) && ( height < 0 ))
             display.setColor( Color.red );

      display.fillRect(x, y, Math.abs(width), Math.abs( height ) );
   }
}

Sample Program

   class TestButton {
      public static void main( String args[] ) {
         Counter x = new Counter( "x" );
         Counter y = new Counter( "y" );
         IncreaseDetector plus = new IncreaseDetector( "Pluses" );
         x.addObserver( plus );
         y.addObserver( plus );
         new ButtonControler( x, 30, 30, 150, 50 );
         new ButtonControler( y, 30, 100, 150, 50 );
         new CounterTextView( plus, "# of increases", 30, 170, 150, 50);
         new RectangleView( x, y, 340, 30 );
      }
   }

Consequences

  • Abstract coupling between Subject and Observer
  • Support for broadcast communication
  • Unexpected updates. Simple change in subject can cause numerous updates, which can be expensive or distracting
  • Updates can take too long. Subject can not perform any work until all observers are done

Implementation Issues

  • Mapping subjects (Observables) to observers
    • Use list in subject
    • Use hash table
  • Observing more than one subject
    • If an observer has more than one subject how does it know which one changed?
    • Pass information in the update method
  • Dangling references to Deleted Subjects
    • Deleting a subject should not produce dangling references in its observers
    • Make the subject notify its observers
    • Deleting the observers is not an option
Tagged with: Design PatternJAVAObject Oriented
 

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 

Looking for something?

Use the form below to search the site:


Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Related News Feeds

Set your Twitter account name in your settings to use the TwitterBar Section.