Open main menu

CDOT Wiki β

Changes

Observer

12,243 bytes added, 22:35, 12 March 2007
no edit summary
__TOC__

== Observer Pattern ==
The Observer is a design pattern used in computer programming. The Observer pattern has two parts, the subject and the observer. The pattern has a one-to-many dependency between a subject object and the observer object(s). All the observer objects are notified and updated when a subject changes state.

== UML Example ==
[[Image:Observer.gif]]


== Structure ==
Subject: Knows its observers and provides an interface for attaching and detaching observers
Observer: Defines an updating interface for objects that should be notified
ConcreteSubject: Stores states of ConcreteObservers objects. Sends a notification when a change has occurred
ConcreteObserver: Maintains a reference to the ConcreteSubjects object. Synchronizes itself with the ConcreteSubject's state.

== Code Examples ==

=== C# .NET ===
<pre>
// Observer pattern -- Structural example

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Observer.Structural
{

// MainApp test application

class MainApp
{
static void Main()
{
// Configure Observer pattern
ConcreteSubject s = new ConcreteSubject();

s.Attach(new ConcreteObserver(s,"X"));
s.Attach(new ConcreteObserver(s,"Y"));
s.Attach(new ConcreteObserver(s,"Z"));

// Change subject and notify observers
s.SubjectState = "ABC";
s.Notify();

// Wait for user
Console.Read();
}
}

// "Subject"

abstract class Subject
{
private ArrayList observers = new ArrayList();

public void Attach(Observer observer)
{
observers.Add(observer);
}

public void Detach(Observer observer)
{
observers.Remove(observer);
}

public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
}

// "ConcreteSubject"

class ConcreteSubject : Subject
{
private string subjectState;

// Property
public string SubjectState
{
get{ return subjectState; }
set{ subjectState = value; }
}
}

// "Observer"

abstract class Observer
{
public abstract void Update();
}

// "ConcreteObserver"

class ConcreteObserver : Observer
{
private string name;
private string observerState;
private ConcreteSubject subject;

// Constructor
public ConcreteObserver(
ConcreteSubject subject, string name)
{
this.subject = subject;
this.name = name;
}

public override void Update()
{
observerState = subject.SubjectState;
Console.WriteLine("Observer {0}'s new state is {1}",
name, observerState);
}

// Property
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
}
}
}
</pre>


=== Java ===
<pre>
import java.util.Observable;
import java.util.Observer;

public class MessageBoard extends Observable {
private String message;

public String getMessage() {
return message;
}

public void changeMessage(String message) {
this.message = message;
setChanged();
notifyObservers(message);
}

public static void main(String[] args) {
MessageBoard board = new MessageBoard();
Student bob = new Student();
Student joe = new Student();
board.addObserver(bob);
board.addObserver(joe);
board.changeMessage("More Homework!");
}
}

class Student implements Observer {
public void update(Observable o, Object arg) {
System.out.println("Message board changed: " + arg);
}
}
</pre>

Slightly more in depth Observer Pattern in Java
<pre>
//[C] 2002 Sun Microsystems, Inc.---
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class RunObserverPattern {
public static void main(String[] arguments) {
System.out.println("Example for the Observer pattern");
System.out.println("This demonstration uses a central observable");
System.out.println(" object to send change notifications to several");
System.out.println(" JPanels in a GUI. Each JPanel is an Observer,");
System.out.println(" receiving notifcations when there has been some");
System.out.println(" change in the shared Task that is being edited.");
System.out.println();

System.out.println("Creating the ObserverGui");
ObserverGui application = new ObserverGui();
application.createGui();
}
}

class Task {
private String name = "";

private String notes = "";

private double timeRequired;

public Task() {
}

public Task(String newName, String newNotes, double newTimeRequired) {
name = newName;
notes = newNotes;
timeRequired = newTimeRequired;
}

public String getName() {
return name;
}

public String getNotes() {
return notes;
}

public double getTimeRequired() {
return timeRequired;
}

public void setName(String newName) {
name = newName;
}

public void setTimeRequired(double newTimeRequired) {
timeRequired = newTimeRequired;
}

public void setNotes(String newNotes) {
notes = newNotes;
}

public String toString() {
return name + " " + notes;
}
}

class TaskChangeObservable {
private ArrayList observers = new ArrayList();

public void addTaskChangeObserver(TaskChangeObserver observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}

public void removeTaskChangeObserver(TaskChangeObserver observer) {
observers.remove(observer);
}

public void selectTask(Task task) {
Iterator elements = observers.iterator();
while (elements.hasNext()) {
((TaskChangeObserver) elements.next()).taskSelected(task);
}
}

public void addTask(Task task) {
Iterator elements = observers.iterator();
while (elements.hasNext()) {
((TaskChangeObserver) elements.next()).taskAdded(task);
}
}

public void updateTask(Task task) {
Iterator elements = observers.iterator();
while (elements.hasNext()) {
((TaskChangeObserver) elements.next()).taskChanged(task);
}
}
}

interface TaskChangeObserver {
public void taskAdded(Task task);

public void taskChanged(Task task);

public void taskSelected(Task task);
}

class TaskEditorPanel extends JPanel implements ActionListener,
TaskChangeObserver {
private JPanel controlPanel, editPanel;

private JButton add, update, exit;

private JTextField taskName, taskNotes, taskTime;

private TaskChangeObservable notifier;

private Task editTask;

public TaskEditorPanel(TaskChangeObservable newNotifier) {
notifier = newNotifier;
createGui();
}

public void createGui() {
setLayout(new BorderLayout());
editPanel = new JPanel();
editPanel.setLayout(new GridLayout(3, 2));
taskName = new JTextField(20);
taskNotes = new JTextField(20);
taskTime = new JTextField(20);
editPanel.add(new JLabel("Task Name"));
editPanel.add(taskName);
editPanel.add(new JLabel("Task Notes"));
editPanel.add(taskNotes);
editPanel.add(new JLabel("Time Required"));
editPanel.add(taskTime);

controlPanel = new JPanel();
add = new JButton("Add Task");
update = new JButton("Update Task");
exit = new JButton("Exit");
controlPanel.add(add);
controlPanel.add(update);
controlPanel.add(exit);
add.addActionListener(this);
update.addActionListener(this);
exit.addActionListener(this);
add(controlPanel, BorderLayout.SOUTH);
add(editPanel, BorderLayout.CENTER);
}

public void setTaskChangeObservable(TaskChangeObservable newNotifier) {
notifier = newNotifier;
}

public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == add) {
double timeRequired = 0.0;
try {
timeRequired = Double.parseDouble(taskTime.getText());
} catch (NumberFormatException exc) {
}
notifier.addTask(new Task(taskName.getText(), taskNotes.getText(),
timeRequired));
} else if (source == update) {
editTask.setName(taskName.getText());
editTask.setNotes(taskNotes.getText());
try {
editTask
.setTimeRequired(Double.parseDouble(taskTime.getText()));
} catch (NumberFormatException exc) {
}
notifier.updateTask(editTask);
} else if (source == exit) {
System.exit(0);
}

}

public void taskAdded(Task task) {
}

public void taskChanged(Task task) {
}

public void taskSelected(Task task) {
editTask = task;
taskName.setText(task.getName());
taskNotes.setText(task.getNotes());
taskTime.setText("" + task.getTimeRequired());
}
}

class TaskHistoryPanel extends JPanel implements TaskChangeObserver {
private JTextArea displayRegion;

public TaskHistoryPanel() {
createGui();
}

public void createGui() {
setLayout(new BorderLayout());
displayRegion = new JTextArea(10, 40);
displayRegion.setEditable(false);
add(new JScrollPane(displayRegion));
}

public void taskAdded(Task task) {
displayRegion.append("Created task " + task + "\n");
}

public void taskChanged(Task task) {
displayRegion.append("Updated task " + task + "\n");
}

public void taskSelected(Task task) {
displayRegion.append("Selected task " + task + "\n");
}
}

class TaskSelectorPanel extends JPanel implements ActionListener,
TaskChangeObserver {
private JComboBox selector = new JComboBox();

private TaskChangeObservable notifier;

public TaskSelectorPanel(TaskChangeObservable newNotifier) {
notifier = newNotifier;
createGui();
}

public void createGui() {
selector = new JComboBox();
selector.addActionListener(this);
add(selector);
}

public void actionPerformed(ActionEvent evt) {
notifier.selectTask((Task) selector.getSelectedItem());
}

public void setTaskChangeObservable(TaskChangeObservable newNotifier) {
notifier = newNotifier;
}

public void taskAdded(Task task) {
selector.addItem(task);
}

public void taskChanged(Task task) {
}

public void taskSelected(Task task) {
}
}

class ObserverGui {
public void createGui() {
JFrame mainFrame = new JFrame("Observer Pattern Example");
Container content = mainFrame.getContentPane();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
TaskChangeObservable observable = new TaskChangeObservable();
TaskSelectorPanel select = new TaskSelectorPanel(observable);
TaskHistoryPanel history = new TaskHistoryPanel();
TaskEditorPanel edit = new TaskEditorPanel(observable);
observable.addTaskChangeObserver(select);
observable.addTaskChangeObserver(history);
observable.addTaskChangeObserver(edit);
observable.addTask(new Task());
content.add(select);
content.add(history);
content.add(edit);
mainFrame.addWindowListener(new WindowCloseManager());
mainFrame.pack();
mainFrame.setVisible(true);
}

private class WindowCloseManager extends WindowAdapter {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
}
}
</pre>

== Drawbacks ==
1. Abstract coupling between subject and observer: All a subject knows is that it has a list of its observers. Because subject and observer aren't tightly coupled, they can belong to different layers of abstraction in a system.
2. Support for broadcast communication: The notification that a subject sends doesn't have to specify its receiver. The subject doesn't care about how many observers are observing it, all it does is a broadcast of its current change of its state.
3. Unexpected updates: A cascade of of updates to observers and their dependent objects may occur when an observer does a change to a subject. This occurs because the observers do not know of other observers.
1
edit