Observer pattern

From Wikipedia, the free encyclopedia

Jump to: navigation, search

The observer pattern (a subset of the asynchronous publish/subscribe pattern) is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems.

Contents

[edit] Participant classes

The participants of the pattern are detailed below. Member functions are listed with bullets.

[edit] Subject

This abstract class provides an interface for attaching and detaching observers. Subject class also holds a private list of observers. Contains these functions (methods):

  • Attach - Adds a new observer to the list of observers observing the subject.
  • Detach - Removes an existing observer from the list of observers observing the subject
  • Notify - Notifies each observer by calling the Update function in the observer, when a change occurs.

The Attach function has an observer as argument. This may be either a virtual function of the observer class (Update in this description) or a function pointer (more generally a function object or functor) in a non object oriented setting.

[edit] ConcreteSubject

The class provides the state of interest to observers. It also sends a notification to all observers, by calling the Notify function in its superclass or base class (i.e., in the Subject class). Contains this function:

  • GetState - Returns the state of the subject.

[edit] Observer

This class defines an updating interface for all observers, to receive update notification from the subject. The Observer class is used as an abstract class to implement concrete observers. Contains this function:

  • Update - An abstract function, to be overridden by concrete observers.

[edit] ConcreteObserver

This class maintains a reference with the ConcreteSubject, to receive the state of the subject when a notification is received. Contains this function:

  • Update - This is the overridden function in the concrete class. When this function is called by the subject, the ConcreteObserver calls the GetState function of the subject to update the information it has about the subject's state.

Each concrete observer implements the update function and as a consequence defines its own behavior when the notification occurs.

When a change occurs to the (concrete) subject, it sends a notification to all observers, by calling the Notify function. The Notify function then calls the Update function of all attached (concrete) observers. The Notify and Update functions may have parameters indicating what kind of change has occurred to the subject. This allows for optimizations in the observer (only updating those parts that changed).

[edit] Typical usages

  • When an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects allows the programmer to vary and reuse them independently.
  • When a change to one object requires changing others, and it's not known in advance how many objects need to be changed.
  • When an object should be able to notify other objects without making assumptions about who these objects are.

The observer pattern is also very often associated with the model-view-controller (MVC) paradigm. In MVC, the observer pattern is used to create a loose coupling between the model and the view. Typically, a modification in the model triggers the notification of model observers which are actually the views.

An example is Java Swing, in which the model is expected to issue change notifications to the views via the PropertyChangeNotification infrastructure. Model classes are Java beans that behave as the subject, described above. View classes are associated with some visible item on the GUI and behave as the observers, described above. As the application runs, changes are made to the model. The user sees these changes because the views are updated accordingly.

[edit] Examples

[edit] Python

The observer pattern in Python:

class AbstractSubject:
    def register(self, listener):
        raise NotImplementedError("Must subclass me")
 
    def unregister(self, listener):
        raise NotImplementedError("Must subclass me")
 
    def notify_listeners(self, event):
        raise NotImplementedError("Must subclass me")
 
class Listener:
    def __init__(self, name, subject):
        self.name = name
        subject.register(self)
 
    def notify(self, event):
        print self.name, "received event", event
 
class Subject(AbstractSubject):
    def __init__(self):
        self.listeners = []
        self.data = None
 
    def getUserAction(self):
        self.data = raw_input('Enter something to do:')
        return self.data
 
    # Implement abstract Class AbstractSubject
 
    def register(self, listener):
        self.listeners.append(listener)
 
    def unregister(self, listener):
        self.listeners.remove(listener)
 
    def notify_listeners(self, event):
        for listener in self.listeners:
            listener.notify(event)
 
 
if __name__=="__main__":
    # make a subject object to spy on
    subject = Subject()
 
    # register two listeners to monitor it.
    listenerA = Listener("<listener A>", subject)
    listenerB = Listener("<listener B>", subject)
 
    # simulated event
    subject.notify_listeners ("<event 1>")
    # outputs:
    #     <listener A> received event <event 1>
    #     <listener B> received event <event 1>
 
    action = subject.getUserAction()
    subject.notify_listeners(action)
    #Enter something to do:hello
    # outputs:
    #     <listener A> received event hello
    #     <listener B> received event hello

The observer pattern can be implemented more succinctly in Python using function decorators.

[edit] Java

Below is an example that takes keyboard input and treats each input line as an event. The example is built upon the library classes java.util.Observer and java.util.Observable. When a string is supplied from System.in, the method notifyObserver is then called, in order to notify all observers of the event's occurrence, in the form of an invocation of their 'update' methods - in our example, ResponseHandler.update(...).

The file myapp.java contains a main() method that might be used in order to run the code.

/* File Name : EventSource.java */
 
package obs;
import java.util.Observable;          //Observable is here
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class EventSource extends Observable implements Runnable 
{
    public void run()
    {
        try
        {   
            final InputStreamReader isr = new InputStreamReader( System.in );
            final BufferedReader br = new BufferedReader( isr );
            while( true )
            {
                final String response = br.readLine();
                setChanged();
                notifyObservers( response );
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
/* File Name: ResponseHandler.java */
 
package obs;
 
import java.util.Observable;
import java.util.Observer;  /* this is Event Handler */
 
public class ResponseHandler implements Observer
{
    private String resp;
    public void update (Observable obj, Object arg)
    {
        if (arg instanceof String)
        {
            resp = (String) arg;
            System.out.println("\nReceived Response: "+ resp );
        }
    }
}
/* Filename : myapp.java */
/* This is main program */
 
package obs;
 
public class MyApp
{
    public static void main(String args[])
    {            
        System.out.println("Enter Text >");
 
        // create an event source - reads from stdin
        final EventSource evSrc = new EventSource();
 
        // create an observer
        final ResponseHandler respHandler = new ResponseHandler();
 
        // subscribe the observer to the event source
        evSrc.addObserver( respHandler );
 
        // starts the event thread
        Thread thread = new Thread(evSrc);
        thread.start();
    }
}

[edit] C# - Traditional Method

C# and the other .NET Framework languages do not typically require a full implementation of the Observer pattern using interfaces and concrete objects. Here is an example of using them, however.

 
 
using System;
using System.Collections;
 
namespace Wikipedia.Patterns.Observer
{
	// IObserver --> interface for the observer
	public interface IObserver
	{
		// called by the subject to update the observer of any change
		// The method parameters can be modified to fit certain criteria
		void Update(string message);
	}
 
	public class Subject
	{
		// use array list implementation for collection of observers
		private ArrayList observers;
 
		// constructor
		public Subject()
		{
			observers = new ArrayList();
		}
 
		public void Register(IObserver observer)
		{
			// if list does not contain observer, add
			if (!observers.Contains(observer))
			{
				observers.Add(observer);
			}
		}
 
		public void Unregister(IObserver observer)
		{
			// if observer is in the list, remove
			if (observers.Contains(observer))
			{
				observers.Remove(observer);
			}
		}
 
		public void Notify(string message)
		{
			// call update method for every observer
			foreach (IObserver observer in observers)
			{
				observer.Update(message);
			}
		}
	}
 
	// Observer1 --> Implements the IObserver
	public class Observer1 : IObserver
	{
		public void Update(string message)
		{
			Console.WriteLine("Observer1:" + message);
		}
	}
 
	// Observer2 --> Implements the IObserver
	public class Observer2 : IObserver
	{
		public void Update(string message)
		{
			Console.WriteLine("Observer2:" + message);
		}
	}
 
	// Test class
	public class ObserverTester
	{
		[STAThread]
		public static void Main()
		{
			Subject mySubject = new Subject();
			IObserver myObserver1 = new Observer1();
			IObserver myObserver2 = new Observer2();
 
			// register observers
			mySubject.Register(myObserver1);
			mySubject.Register(myObserver2);
 
			mySubject.Notify("message 1");
			mySubject.Notify("message 2");
		}
	}
}

[edit] C# - Using Events

The alternative to using concrete and abstract observers and publishers in C# and other .NET Framework languages, such as Visual Basic, is to use events. The event model is supported via delegates that define the method signature that should be used to capture events. Consequently, delegates provide the mediation otherwise provided by the abstract observer, the methods themselves provide the concrete observer, the concrete subject is the class defining the event, and the subject is the event system built into the base class library. It is the preferred method of accomplishing the Observer pattern in .NET applications.

using System;
 
// First, declare a delegate type that will be used to fire events.
//  This is the same delegate as System.EventHandler.
//  This delegate serves as the abstract observer.
//  It does not provide the implementation, but merely the contract.
public delegate void EventHandler(object sender, EventArgs e);
 
// Next, declare a published event.  This serves as the concrete subject.
//  Note that the abstract subject is handled implicitly by the runtime.
public class Button
{
    // The EventHandler contract is part of the event declaration.
    public event EventHandler Clicked;
 
    // By convention, .NET events are fired from descendant classes by a virtual method,
    //  allowing descendant classes to handle the event invocation without subscribing
    //  to the event itself.
    protected virtual void OnClicked(EventArgs e)
    {
        if (Clicked != null) 
            Clicked(this, e); // implicitly calls all observers/subscribers
    }
}
 
// Then in an observing class, you are able to attach and detach from the events:
public class Window
{
    private Button okButton;
 
    public Window()
    {
        okButton = new Button();
        // This is an attach function.  Detaching is accomplished with -=.
        // Note that it is invalid to use the assignment operator - events are multicast
        //  and can have multiple observers.
        okButton.Clicked += new EventHandler(okButton_Clicked);
    }
 
    private void okButton_Clicked(object sender, EventArgs e)
    {
        // This method is called when Clicked(this, e) is called within the Button class
        //  unless it has been detached.
    }
}

[edit] C++

#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
 
// The Abstract Observer
class ObserverBoardInterface
{
public:
    virtual void update(float a,float b,float c) = 0;
};
 
// Abstract Interface for Displays
class DisplayBoardInterface
{
public:
    virtual void show() = 0;
};
 
// The Abstract Subject
class WeatherDataInterface
{
public:
    virtual void registerOb(ObserverBoardInterface* ob) = 0;
    virtual void removeOb(ObserverBoardInterface* ob) = 0;
    virtual void notifyOb() = 0;
};
 
// The Concrete Subject
class ParaWeatherData: public WeatherDataInterface
{
public:
    void SensorDataChange(float a,float b,float c)
    {
        m_humidity = a;
        m_temperature = b;
        m_pressure = c;
        notifyOb();
    }
 
    void registerOb(ObserverBoardInterface* ob)
    {
        m_obs.push_back(ob);
    }
 
    void removeOb(ObserverBoardInterface* ob)
    {
        m_obs.remove(ob);
    }
protected:
    void notifyOb()
    {
        list<ObserverBoardInterface*>::iterator pos = m_obs.begin();
        while (pos != m_obs.end())
        {
            ((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);
            (dynamic_cast<DisplayBoardInterface*>(*pos))->show();
            ++pos;
        }
    }
 
private:
    float        m_humidity;
    float        m_temperature;
    float        m_pressure;
    list<ObserverBoardInterface* > m_obs;
};
 
// A Concrete Observer
class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
    CurrentConditionBoard(ParaWeatherData& a):m_data(a)
    {
        m_data.registerOb(this);
    }
    void show()
    {
        cout<<"_____CurrentConditionBoard_____"<<endl;
        cout<<"humidity: "<<m_h<<endl;
        cout<<"temperature: "<<m_t<<endl;
        cout<<"pressure: "<<m_p<<endl;
        cout<<"_______________________________"<<endl;
    }
 
    void update(float h, float t, float p)
    {
        m_h = h;
        m_t = t;
        m_p = p;
    }
 
private:
    float m_h;
    float m_t;
    float m_p;
    ParaWeatherData& m_data;
};
 
// A Concrete Observer
class StatisticBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
    StatisticBoard(ParaWeatherData& a):m_maxt(-1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)
    {
        m_data.registerOb(this);
    }
 
    void show()
    {
        cout<<"________StatisticBoard_________"<<endl;
        cout<<"lowest  temperature: "<<m_mint<<endl;
        cout<<"highest temperature: "<<m_maxt<<endl;
        cout<<"average temperature: "<<m_avet<<endl;
        cout<<"_______________________________"<<endl;
    }
 
    void update(float h, float t, float p)
    {
        ++m_count;
        if (t>m_maxt)
        {
            m_maxt = t;
        }
        if (t<m_mint)
        {
            m_mint = t;
        }
        m_avet = (m_avet * (m_count-1) + t)/m_count;
    }
 
private:
    float m_maxt;
    float  m_mint;
    float m_avet;
    int m_count;
    ParaWeatherData& m_data;
};
 
 
int main(int argc, char *argv[])
{
 
    ParaWeatherData * wdata = new ParaWeatherData;
    CurrentConditionBoard* currentB = new CurrentConditionBoard(*wdata);
    StatisticBoard* statisticB = new StatisticBoard(*wdata);
 
    wdata->SensorDataChange(10.2, 28.2, 1001);
    wdata->SensorDataChange(12, 30.12, 1003);
    wdata->SensorDataChange(10.2, 26, 806);
    wdata->SensorDataChange(10.3, 35.9, 900);
 
    wdata->removeOb(currentB);
 
    wdata->SensorDataChange(100, 40, 1900);  
 
    delete statisticB;
    delete currentB;
    delete wdata;
 
    return 0;
}

[edit] AS3

// Main Class
package {
    import flash.display.MovieClip;
 
    public class Main extends MovieClip {
        private var _cs:ConcreteSubject = new ConcreteSubject();
        private var _co1:ConcreteObserver1 = new ConcreteObserver1();
        private var _co2:ConcreteObserver2 = new ConcreteObserver2();
 
        public function Main() {
            _cs.registerObserver(_co1);
            _cs.registerObserver(_co2);
 
            _cs.changeState(10);
            _cs.changeState(99);
 
            _cs.unRegisterObserver(_co1);
 
            _cs.changeState(17);
 
            _co1 = null;
        }
    }
}
 
// Interface Subject
package {
    public interface ISubject {
        function registerObserver(o:IObserver):void;
 
        function unRegisterObserver(o:IObserver):void;
 
        function updateObservers():void;
 
        function changeState(newState:uint):void;
    }
}
 
// Interface Observer
package {
    public interface IObserver {
        function update(newState:uint):void;
    }
}
 
// Concrete Subject
package {
    public class ConcreteSubject implements ISubject {
        private var _observersList:Array = new Array();
        private var _currentState:uint;
 
        public function ConcreteSubject() {
        }
 
        public function registerObserver(o:IObserver):void {
            _observersList.push( o );
            _observersList[_observersList.length-1].update(_currentState); // update newly registered
        }
 
        public function unRegisterObserver(o:IObserver):void {
            _observersList.splice( _observersList.indexOf( o ), 1 );
        }
 
        public function updateObservers():void {
            for( var i:uint = 0; i<_observersList.length; i++) {
                _observersList[i].update(_currentState);
            }
        }
 
        public function changeState(newState:uint):void {
            _currentState = newState;
            updateObservers();
        }
    }
}
 
// Concrete Observer 1
package {
    public class ConcreteObserver1 implements IObserver {
        public function ConcreteObserver1() {
        }
 
        public function update(newState:uint):void {
            trace( "co1: "+newState );
        }
 
        // other Observer specific methods
    }
}
 
// Concrete Observer 2
package {
    public class ConcreteObserver2 implements IObserver {
        public function ConcreteObserver2() {
        }
 
        public function update(newState:uint):void {
            trace( "co2: "+newState );
        }
 
        // other Observer specific methods
    }
}

[edit] PHP

class STUDENT

<?php
class Student implements SplObserver {
 
	protected $type = "Student";
	private   $name;
	private   $address;
	private   $telephone;
	private   $email;
	private   $_classes = array();
 
	public function __construct($name)
	{
		$this->name = $name;
	}
 
	public function GET_type()
	{
		return $this->type;
	}
 
	public function GET_name()
	{
		return $this->name;
	}
 
	public function GET_email()
	{
		return $this->email;
	}
 
	public function GET_telephone()
	{
		return $this->telephone;
	}
 
	public function update(SplSubject $object)
	{
		$object->SET_log("Comes from ".$this->name.": I'm a student of ".$object->GET_materia());
	}
 
}
 
?>

class TEACHER

<?php
class Teacher implements SplObserver {
 
	protected $type = "Teacher";
	private   $name;
	private   $address;
	private   $telephone;
	private   $email;
	private   $_classes = array();
 
	public function __construct($name)
	{
		$this->name = $name;
	}
 
	public function GET_type()
	{
		return $this->type;
	}
 
	public function GET_name()
	{
		return $this->name;
	}
 
	public function GET_email()
	{
		return $this->email;
	}
 
	public function GET_telephone()
	{
		return $this->name;
	}
 
	public function update(SplSubject $object)
	{
		$object->SET_log("Comes from ".$this->name.": I teach in ".$object->GET_materia());
	}
 
}
 
?>

Class SUBJECT

<?php
 
class Subject implements SplSubject {
 
	private $name_materia;
	private $_observers = array();
	private $_log = array();
 
	function __construct($name)
	{
		$this->name_materia = $name;
		$this->_log[]       = "Subject $name was included";
	}
 
	/* Add an observer */
	public function attach(SplObserver $classes) {
		$this->_classes[] = $classes;
		$this->_log[]     = " The ".$classes->GET_type()." ".$classes->GET_name()." was included";
	}
 
	/* Remove an observer */
	public function detach(SplObserver $classes) {
		foreach ($this->_classes as $key => $obj) {
			if ($obj == $classes) {
				unset($this->_classes[$key]);
				$this->_log[] = " The ".$classes->GET_type()." ".$classes->GET_name()." was removed";
            		}
		}
	}
 
	/* Notificate an observer */
	public function notify(){
		foreach ($this->_classes as $classes){
			$classes->update($this);
		}
	}
 
	public function GET_materia()
	{
		return $this->name_materia;
	}
 
	function SET_log($valor)
	{
		$this->_log[] = $valor ;
	}
 
	function GET_log()
	{
		return $this->_log;
	}
 
 
}
?>

Application

<?php
require_once("teacher.class.php");
require_once("student.class.php");
require_once("subject.class.php");
 
$subject  = new Subject("Math");
$marcus   = new Teacher("Marcus Brasizza");
$rafael   = new Student("Rafael");
$vinicius = new Student("Vinicius");
 
// Include observers in the math Subject
$subject->attach($rafael);
$subject->attach($vinicius);
$subject->attach($marcus);
 
$subject2 = new Subject("English");
$renato   = new Teacher("Renato");
$fabio    = new Student("Fabio");
$tiago    = new Student("Tiago");
 
// Include observers in the english Subject
$subject2->attach($renato);
$subject2->attach($vinicius);
$subject2->attach($fabio);
$subject2->attach($tiago);
 
// Remove the instance "Rafael from subject"
$subject->detach($rafael);
 
// Notify both subjects
$subject->notify();
$subject2->notify();
 
echo "First Subject <br>";
echo "<pre>";
print_r($subject->GET_log());
echo "</pre>";
echo "<hr>";
echo "Second Subject <br>";
echo "<pre>";
print_r($subject2->GET_log());
echo "</pre>";
?>

OUTPUT

First Subject

Array
(
    [0] =>  Subject Math was included
    [1] =>  The Student Rafael was included
    [2] =>  The Student Vinicius was included
    [3] =>  The Teacher Marcus Brasizza was included
    [4] =>  The Student Rafael was removed
    [5] => Comes from Vinicius: I'm a student of Math
    [6] => Comes from Marcus Brasizza: I teach in Math
)

Second Subject

Array
(
    [0] =>  Subject English was included
    [1] =>  The Teacher Renato was included
    [2] =>  The Student Vinicius was included
    [3] =>  The Student Fabio was included
    [4] =>  The Student Tiago was included
    [5] => Comes from Renato: I teach in English
    [6] => Comes from Vinicius: I'm a student of English
    [7] => Comes from Fabio: I'm a student of English
    [8] => Comes from Tiago: I'm a student of English
)

[edit] Ruby

In Ruby, use the standard Observable mixin. For documentation and an example, see http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html

[edit] Implementations

The observer pattern is implemented in numerous programming libraries and systems, including almost all GUI toolkits.

Some of the most notable implementations of this pattern:

[edit] References

[edit] See also

[edit] External links

Personal tools