Вы находитесь на странице: 1из 10

DESIGN PATTERNS 4

Suppose we want to create an application that allows two people to watch on their mobile
phones two different types of presentations for the results of a student team in a competition.
Each student in the team will get a theory score and a practice score that will be used to
compute the average theory and practice scores of the team.
The results can be used by one type of displayer to show the current scores and by the other
type of displayer to show the trends of these scores.

A first try to implement this simple application is to use a class for the Score and two
classes, CurrentScoreDisplay and TrendDisplay, for the two displayers, as in the following
class diagram:

The implementation code will look like:

public class Score{


int th;
int pr;
int nrStud;
CurrentScoreDisplay csd;
TrendDisplay td;

public Score(CurrentScoreDisplay csd, TrendDisplay td){


this.csd = csd;
this.td = td;
}

public setScore (int th, int pr)


{
nrStud=nrStud+1;
this.th = ((nrStud-1)this.th+th)/nrStud;
this.pr = ((nrStud-1)this.pr+pr)/nrStud;
scoreChanged();
}

public scoreChanged ()
{
csd.update(int th, int pr);
td.update(int th, int pr);
}
}

public class CurrentScoreDisplay{


int th;
int pr;

public void update(int th, int pr)


{
System.out.println(current theory score = +th);
System.out.println(current practice score = +pr);
}
}

public class TrendDisplay{


int th;
int pr;

public void update(int th, int pr)


{
if(this.th-th<0)
System.out.println(Better at theory);
else
System.out.println(Worse at theory);
if(this.pr-pr<0)
System.out.println(Better at practice);
else
System.out.println(Worse at practice);
this.th = th;
this.pr = pr;

}
}

This approach has some important drawbacks:


- the coding is realized to concrete implementations not to interfaces
- part that changes is not encapsulated
- display elements can not be added/removed at runtime.

If we try to expand the application with other displayers of the existing types or with new
types of displayers it will be difficult because code of the existing classes must be altered.

In order to eliminate these drawbacks we appeal to the publisher-subscriber metaphor.

In this metaphor a publisher publishes a magazine and a subscriber automatically gets each
new edition of the magazine. The subscriber can also unsubscribe from the publisher and will get
no other magazine.
This metaphor is the basis for the OBSERVER design pattern.

Excerpt from catalogue definition:

Name:
OBSERVER
Intent:
Defines a one-to-many dependency between objects so that when one object changes
its state, all of its dependants are notified and updated automatically.
Structure:

Participants:
Subject (the publisher from the metaphor) publishes its state.
Observer (the subscriber from the metaphor) dependant, automatically notified and updated
when the state of the Subject changes.

When data in Subject changes, each Observer is notified.


Observer has registered (subscribed to) with the Subject to receive updates when the data in
the Subject changes.

Aplicability when:
- an abstraction has two aspects, one dependent on the other; encapsulating these aspects
in separate objects lets you vary and reuse them independently.
- a change to one object requires changing others, and you do not know how many objects
need to be changed.
- An object should be able to notify other objects without making assumptions about who
these objects are; in other words, you do not want these objects tightly coupled.
GOOD OO DESIGN PRACTICES:
P4. Strive for loosely coupled designs between objects that interact.

Advantages of OBSERVER:
1. Conforms to P4. Loose coupling characterizes the interaction between objects that have little
knowledge of each other.

2. Through Observer interface, observers can be


- added
- removed
- replaced
at runtime.

Applying OBSERVER design pattern to our example.

For more flexibility we added another interface responsible to define display operation. It is not part
of the Observer pattern.

IMPLEMENTATION although Java (and other OO languages) support exists for Observer design
pattern, in a lot of cases is more flexible to implement your own structure.

public interface Subject {


public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}

public interface Observer {


public void update(int th, int pr);
}

public interface DisplayElement {


public void display(int th, int pr);
}

public class Score implements Subject {


private ArrayList observers;
private int th;
private int pr;
private int nrStud;

public Score() { observers = new ArrayList();}

public void registerObserver(Observer o){


observers.add(o);}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>=0){observers.remove(i);}}
public void notifyObservers() {
for (int i=0; i<observers.size(); i++) {
Observer ob = (Observer)observers.get(i);
ob.update(th, pr);
}

public void scoreChanged() {notifyObservers();}

public setScore (int th, int pr)


{
nrStud=nrStud+1;
this.th = ((nrStud-1)this.th+th)/nrStud;
this.pr = ((nrStud-1)this.pr+pr)/nrStud;
scoreChanged();
}

public class CurrentScoreDisplay implements Observer, DisplayElement{


private int th;
private int pr;
private Subject s;

public CurrentScoreDisplay(Subject s){


this.s = s;
s.registerObserver(this);
}

public void update(int th, int pr)


{
this.th = th;
this.pr = pr;
display();
}

public void display() {


System.out.println(current theory score = +th);
System.out.println(current practice score = +pr);
}
}

public class TrendDisplay{


int th;
int pr;
int th_p;
int pr_p;
private Subject s;

public TrendDisplay(Subject s){


this.s = s;
s.registerObserver(this);
}

public void update(int th, int pr)


{
this.th = th;
this.pr = pr;
display();
}

public void display() {


if(th_p-th<0)
System.out.println(Better at theory);
else
System.out.println(Worse at theory);
if(pr_p-pr<0)
System.out.println(Better at practice);
else
System.out.println(Worse at practice);
}
}

And now let see how the pattern works. In order to test it we implement a class
Competition as follows:

public class Competition{


public static void main(String[] args) {
Score s = new Score();
CurrentScoreDisplay csd = new CurrentScoreDisplay(s);
TrendDisplay td = new TrendDisplay(s);

s.setScore(7,8);
wait(2000);
s.setScore(5,9);
wait(2000);
s.setScore(10,9);
}
}

First an object of Score type is instantiated, to be the subject. Then two displayers
(CurrentScoreDisplay and TrendDisplay)are instantiated, and to their constructors a
reference to the Score object is given. This reference is used by the displayers to register as
observers to this subject.
Then the state of the Score object is changed from time to time, when calling the method
setScore() on it. Besides updating the values of the instance variables in the Score object, this
method will also call scoreChanged() which in turn calls notifyObservers().
Finally, the method notifyObservers() will call update() on all observeres registered to
the Score, so that all of them will update their values about the state of the Score and will display
this change correspondingly.
If we try now to expand the application with other displayers of the existing types we can
simply instantiate new objects of CurrentScoreDisplay type and/or of TrendDisplay type.

If we want to expand the application with new types of displayers it will be also simple. We
must first define separately the new type of displayer which will implement the Observer interface
similar to the other displayer types, and will implement the Display interface in its specific
manner. Then we can use it as previously described (as when expanding the application with
displayers of existing types).

You can find a free on-line book on design patterns at:


http://sourcemaking.com/design_patterns/

Optional:
JAVA support for implementing OBSERVER design pattern.

Java offers support for implementing OBSERVER design pattern through the use of two
interfaces: java.util.Observer and java.util.Observable.

The following information is found at:


http://publib.boulder.ibm.com/infocenter/wsadhelp/v5r1m2/index.jsp?
topic=/com.sun.api.doc/java/util/Observer.html

Interface java.util.Observer
public interface Observer

A class can implement the Observer interface when it wants to be informed of changes in observable objects.
See Also
o Observable
Method Index
Method Description

void update(Observable, This method is called whenever the


Object) observed object is changed.

Methods
update
public void update(Observable o,
Object arg)
This method is called whenever the observed object is changed. An application calls an Ob-
servable object's notifyObservers method to have all the object's observers notified of the
change.
Parameters
o o - the observable object.
o arg - an argument passed to the notifyObservers method.
Class java.util.Observable
java.lang.Object
java.util.Observable
public class Observable
extends Object
This class represents an observable object, or "data" in the model-view paradigm. It can be subclassed to
represent an object that the application wants to have observed.

An observable object can have one or more observers. An observer may be any object that implements in-
terface Observer. After an observable instance changes, an application calling the Observable's notifyObservers
method causes all of its observers to be notified of the change by a call to their update method.

The order in which notifications will be delivered is unspecified. The default implementation provided in the
Observerable class will notify Observers in the order in which they registered interest, but subclasses may
change this order, use no guaranteed order, deliver notifications on separate threaads, or may guarantee
that their subclass follows this order, as they choose.

Note that this notification mechanism is has nothing to do with threads and is completely separate from the
wait and notify mechanism of class Object.

When an observable object is newly created, its set of observers is empty. Two observers are considered the
same if and only if the equals method returns true for them.
See Also
o notifyObservers
o notifyObservers
o Observer
o update
Constructor Index
Constructor Description

Observable() Construct an Observable with zero Observers.

Method Index
Method Description
Adds an observer to the set of observers
void for this object, provided that it is not the
addObserver(Observer) same as some observer already in the set.
Indicates that this object has no longer
changed, or that it has already notified all
of its observers of its most recent change,
so that the hasChanged method will now
void clearChanged() return false.
Returns the number of observers of this
int countObservers() Observable object.
void
deleteObserver(Observ Deletes an observer from the set of
er) observers of this object.
Clears the observer list so that this object
void deleteObservers() no longer has any observers.

boolean hasChanged() Tests if this object has changed.


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
void notifyObservers () longer changed.
void If this object has changed, as indicated by
notifyObservers(Object the hasChanged method, then notify all of its
) observers and then call the clearChanged
Method Index
Method Description
method to indicate that this object has no
longer changed.
Marks this Observable object as having
been changed; the hasChanged method will
void setChanged() now return true.

Constructors
Observable
public Observable()
Construct an Observable with zero Observers.

Methods
addObserver
public synchronized void addObserver(Observer o)
Adds an observer to the set of observers for this object, provided that it is not the same as
some observer already in the set. The order in which notifications will be delivered to mul-
tiple observers is not specified. See the class comment.
Parameters
o o - an observer to be added.

clearChanged
protected synchronized void clearChanged()
Indicates that this object has no longer changed, or that it has already notified all of its ob-
servers of its most recent change, so that the hasChanged method will now return false. This
method is called automatically by the notifyObservers methods.
See Also
o notifyObservers
o notifyObservers
countObservers
public synchronized int countObservers()
Returns the number of observers of this Observable object.
Returns
o the number of observers of this object.

deleteObserver
public synchronized void deleteObserver(Observer o)
Deletes an observer from the set of observers of this object.
Parameters
o o - the observer to be deleted.

deleteObservers
public synchronized void deleteObservers()
Clears the observer list so that this object no longer has any observers.

hasChanged
public synchronized boolean hasChanged()
Tests if this object has changed.
Returns
o true if and only if the setChanged method has been called more recently than
the clearChanged method on this object; false otherwise.
See Also
o clearChanged
o setChanged
notifyObservers
public void notifyObservers()
If this object has changed, as indicated by the hasChanged method, then notify all of its ob-
servers 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
null. In other words, this method is equivalent to:

notifyObservers(null)
See Also
o clearChanged
o hasChanged
o update
notifyObservers
public void notifyObservers(Object arg)
If this object has changed, as indicated by the hasChanged method, then notify all of its ob-
servers 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.
Parameters
o arg - any object.
See Also
o clearChanged
o hasChanged
o update
setChanged
protected synchronized void setChanged()
Marks this Observable object as having been changed; the hasChanged method will now return
true.