CS 635: Advanced Object-Oriented Design & Programming |
---|
References | slide # 1 |
Observer | slide # 2 |
...Structure | slide # 3 |
...Collaborations | slide # 4 |
...SpreadSheet Example | slide # 5 |
...Java's Implementation | slide # 6 |
...A Java Example | slide # 8 |
......The Classes | slide # 8 |
......Sample Program | slide # 20 |
...Consequences | slide # 22 |
...Implementation Issues | slide # 23 |
Java API
Use the Observer 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
Whenever the Observable instance changes,
it notifies all of its observers
Notification is done by calling the update() method on all observers.
/** * 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 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(); } }
/** * 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(); }
/** * 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(); } }
/** * 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(); } }
/** * 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(); } }
/** * 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 ); }
/** * 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(); } }
/** * 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 ) ); } }
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 ); } }
Support for broadcast communication
Unexpected updates
Pass information in the update method
class Counter extends Observable
It might be inefficient or cause too many screen updates
Have clients call Notify at the right time
class Counter extends Observable
{ Counter pageHits = new Counter(); pageHits.increase(); pageHits.increase(); pageHits.increase();
pageHits.notifyObservers(); }
class ComplexObservable extends Observable { Widget frontPart = new Widget(); Gadget internalPart = new Gadget(); public void trickyChange() { frontPart.widgetChange(); internalpart.anotherChange(); setChanged(); notifyObservers( ); } } class MyBetterClass extends ComplexObservable { Gear backEnd = new Gear(); public void trickyChange() { super.trickyChange(); backEnd.yetAnotherChange(); setChanged(); notifyObservers( ); } }
class ComplexObservable extends Observable { Widget frontPart = new Widget(); Gadget internalPart = new Gadget(); /** * Trigger update to observers */ public void trickyChange() { doThisChangeWithFactoryMethod(); setChanged(); notifyObservers( ); } private void doThisChangeWithTemplateMethod() { frontPart.widgetChange(); internalpart.anotherChange(); } } class MyBetterClass extends ComplexObservable { Gear backEnd = new Gear(); private void doThisChangeWithTemplateMethod() { backEnd.yetAnotherChange(); } }
class IncreaseDetector extends Counter implements Observer { // stuff not shown public void update( Observable whatChanged, Object message) { if ( message.equals( INCREASE) ) increase(); } }
class Counter extends Observable { // some code removed public void increase() { count++; setChanged(); notifyObservers( INCREASE ); } }
class IncreaseDetector extends Counter implements Observer { // stuff not shown public void update( Observable whatChanged ) { if ( whatChanged.didYouIncrease() ) increase(); } }