Observer | slide # 1 |
...Java Example | slide # 1 |
......The Classes | slide # 2 |
......Class Descriptions | slide # 3 |
......class Counter | slide # 5 |
......class IncreaseDetector | slide # 6 |
......abstract class CounterButton | slide # 7 |
......class IncreaseButton | slide # 8 |
......class DecreaseButton | slide # 8 |
......class CounterView | slide # 9 |
......class CounterTextView | slide # 10 |
......class ButtonController | slide # 11 |
......class RectangleView | slide # 12 |
......Sample Program | slide # 14 |
......Runtime Object Interaction | slide # 15 |
...Implementation Issues | slide # 16 |
......Mapping subjects(Observables) to observers | slide # 16 |
......Observing more than one subject | slide # 16 |
......Who Triggers the update? | slide # 17 |
......Make sure Subject is self consistent before Notification | slide # 18 |
......Adding information about the change | slide # 20 |
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
/** * A counter can increase or decrease by one. 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 the button 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 the button 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 of the rectangle, 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 ); } }
Pass information in the update method
class Counter extends Observable
It might be inefficient or cause too many screen updates
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 doThisChangeWithFactoryMethod() { frontPart.widgetChange(); internalpart.anotherChange(); } } class MyBetterClass extends ComplexObservable { Gear backEnd = new Gear(); private void doThisChangeWithFactoryMethod() { 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(); } }