11/10/2011

Observer Pattern Sample


Observer is a behavioral 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.

Observer - defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically (the definition from Head First Design Patterns).

Here's a simple Java implementation of this pattern. Also we can extend java.util.Observable class and implement java.util.Observer interface to build observers.

SimpleKeyListener.java:

package observer;

public class SimpleKeyListener implements KeyListener{
    
    public void keyPressed(char key) {
        System.out.println("KeyListener: key pressed..." + key);
    }
    
    public void keyReleased(char key) {
        System.out.println("KeyListener: key released..." + key);
    }
    
    public void keyTyped(char key) {
        System.out.println("KeyListener: key typed..." + key);
    }
    
}

interface KeyListener {
    public void keyPressed(char key);
    public void keyReleased(char key);
    public void keyTyped(char key);
}
package observer;

import java.util.Observable;
import java.util.Observer;

public class SimpleKeyListener implements KeyListener, Observer{
    
    public void keyPressed(char key) {
        System.out.println("KeyListener: key pressed..." + key);
    }
    
    public void keyReleased(char key) {
        System.out.println("KeyListener: key released..." + key);
    }
    
    public void keyTyped(char key) {
        System.out.println("KeyListener: key typed..." + key);
    }

    @Override
    public void update(Observable o, Object obj) {
        if (obj instanceof KeyEvent) {
            KeyEvent keyEvent = (KeyEvent) obj;
            switch (keyEvent.getAction()) {
                case KEY_PRESSED: keyPressed(keyEvent.getCharacter()); break;
                case KEY_RELEASED: keyReleased(keyEvent.getCharacter()); break;
                case KEY_TYPED: keyTyped(keyEvent.getCharacter()); break;
            }
        }
    }
    
}

interface KeyListener {
    public void keyPressed(char key);
    public void keyReleased(char key);
    public void keyTyped(char key);
}

enum KeyEventAction {
    KEY_PRESSED, KEY_RELEASED, KEY_TYPED
}

class KeyEvent {
    
    private KeyEventAction action;
    private char character;
    
    public KeyEvent(KeyEventAction action, char character) {
        super();
        this.action = action;
        this.character = character;
    }
    
    public KeyEventAction getAction() {
        return action;
    }
    
    public char getCharacter() {
        return character;
    }
    
}

Component.java:

package observer;

import java.util.HashSet;
import java.util.Set;

public class Component {
    
    Set<KeyListener> keyListenerList = new HashSet<KeyListener>();
    
    public void addKeyListener(KeyListener keyListener) {
        keyListenerList.add(keyListener);
    }
    public void removeKeyListener(KeyListener keyListener) {
        keyListenerList.remove(keyListener);
    }
    
    
    public void pressKey(char key) {
        // Do work
        System.out.println("Component: pressing key..." + key);
        
        // Send keybords events
        for (KeyListener keyListener : keyListenerList) {
            keyListener.keyPressed(key);
        }
    }
    
    public void releaseKey(char key) {
        // Do work
        System.out.println("Component: releasing key..." + key);
        
        // Send keybords events
        for (KeyListener keyListener : keyListenerList) {
            keyListener.keyReleased(key);
        }
    }
    
    public void typeKey(char key) {
        // Do work
        System.out.println("Component: typing key..." + key);
        
        // Send keybords events
        for (KeyListener keyListener : keyListenerList) {
            keyListener.keyTyped(key);
        }
    }

}
package observer;

import java.util.Observable;

public class Component extends Observable{ 
 
 
 
 
 
   
    public void pressKey(char key) {
        // Do work
        System.out.println("Component: pressing key..." + key);

        // Send keybords events
        setChanged();
        notifyObservers(new KeyEvent(KeyEventAction.KEY_PRESSED, key));

    }
    
    public void releaseKey(char key) {
        // Do work
        System.out.println("Component: releasing key..." + key);
        
        // Send keybords events
        setChanged();
        notifyObservers(new KeyEvent(KeyEventAction.KEY_RELEASED, key));
    }
    
    public void typeKey(char key) {
        // Do work
        System.out.println("Component: typing key..." + key);
        
        // Send keybords events
        setChanged();
        notifyObservers(new KeyEvent(KeyEventAction.KEY_TYPED, key));
    }

}

Main.java:

package observer;

public class Main {

    public static void main(String[] args) {
        Component component = new Component();
        component.addKeyListener(new SimpleKeyListener());
        
        component.pressKey('a');
        component.releaseKey('b');
        
        component.addKeyListener(new SimpleKeyListener());
        
        component.pressKey('c');
        component.releaseKey('d');
        
    }

}
package observer;

public class Main {

    public static void main(String[] args) {
        Component component = new Component();
        component.addObserver(new SimpleKeyListener());
        
        component.pressKey('a');
        component.releaseKey('b');
        
        component.addObserver(new SimpleKeyListener());
        
        component.pressKey('c');
        component.releaseKey('d');
        
    }

}

Output:

Component: pressing key...a
KeyListener: key pressed...a
Component: releasing key...b
KeyListener: key released...b

Component: pressing key...c
KeyListener: key pressed...c
KeyListener: key pressed...c
Component: releasing key...d
KeyListener: key released...d
KeyListener: key released...d
Component: pressing key...a
KeyListener: key pressed...a
Component: releasing key...b
KeyListener: key released...b

Component: pressing key...c
KeyListener: key pressed...c
KeyListener: key pressed...c
Component: releasing key...d
KeyListener: key released...d
KeyListener: key released...d

Examples of using the Observer Pattern

  • Observer Pattern is widely used in Swing based applications. 
  • Another example. Consider that you are programming a stock market application. You'd like to know when the price of a stock changes. Instead of querying the price for each stock, register your observer to a central stock broker then have him notify you every time a price changes.
  • Listener pattern is used in the JVM itself quite a lot. For instance in Java Management Extensions (JMX) you can register asynchronous listeners (named notifications) which JVM will issue whenever some condition is met (like low memory).

No comments:

Post a Comment