The Observer Design Pattern as supported by Java

An example of the Observer Design Pattern with thesimplest model I can devise. (Note: imports removed)

publicclassSimpleModelextends Observable {

privateintcounter;

publicSimpleModel() {counter = 0; }

publicintcurrentCount() {returncounter; }

publicvoid increment() {

counter++;

// Do NOT forget to tell yourself the state has changed

this.setChanged();

// Otherwise, the next notifyObservers message will not send update messages to Observers

this.notifyObservers();

}

}

// One View that draws lines to represent a number|||| for 4 for example

publicclassViewOneextendsJPanelimplements Observer {

privateintnumberOfLinesToDraw;

publicViewOne() {

this.setBackground(Color.GREEN);

}

// The 2nd argument is ignored

publicvoid update(Observable theNotifier, Object completeFrame) {

// Cast now or later

SimpleModeltheModel = (SimpleModel) theNotifier;

numberOfLinesToDraw = theModel.currentCount();

this.repaint();

}

publicvoidpaintComponent(Graphics g) {

super.paintComponent(g);

intxPosition = 5;

for (intc = 1; c=numberOfLinesToDraw; c++) {

g.drawLine(xPosition, 5, xPosition, 25);

xPosition += 5;

}

}

}

publicclassViewTwoextendsJPanelimplements Observer {

private String stringToDrawOnPanel = "0";

publicViewTwo() {

this.setBackground(Color.YELLOW);

this.setFont(new Font("Courier", Font.BOLD, 20));

}

// The 2nd argument is ignored

publicvoid update(Observable theNotifier, Object completeFrame) {

SimpleModeltheModel = (SimpleModel) theNotifier;

stringToDrawOnPanel = "" + theModel.currentCount();

this.repaint();

}

publicvoidpaintComponent(Graphics g) {

super.paintComponent(g);

g.drawString(stringToDrawOnPanel, 4, 24);

}

//This is the application. It contains references to the model and both views.

// This application also has an inner class UpdateButtonListeneras the Controller

importjava.awt.Container;

importjava.awt.event.ActionEvent;

importjava.awt.event.ActionListener;

importjava.util.Observer;

importjavax.swing.JButton;

importjavax.swing.JFrame;

importjavax.swing.JPanel;

publicclassTwoObserversMainextendsJFrame {

publicstaticvoid main(String[] args) {

newTwoObserversMain().setVisible(true);

}

publicTwoObserversMain() {

layoutThisJFrame();

registerListeners();

setUpModelAndObservers();

}

privateSimpleModeltheModel;

privateJPanelview1 = newViewOne(); // The views could also be Observer

privateJPanelview2 = newViewTwo();

privateJPanelcurrentView = view1; // The default Observer

privateJButtonupDateButton = newJButton("Increment the model by 1");

privateJButtonbuttonOne = newJButton("View One");

privateJButtonbuttonTwo = newJButton("View Two");

// cp: the place where inner classes can add, remove, repaint, and validate layout

private Container cp = getContentPane();

publicvoidlayoutThisJFrame() {

setTitle("Two Observers");

setSize(260, 200);

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setLayout(null);

view1.setSize(222, 30);

view1.setLocation(10, 60);

view2.setSize(222, 30);

view2.setLocation(10, 60);

upDateButton.setSize(200, 30);

upDateButton.setLocation(20, 10);

add(upDateButton);

cp.add((JPanel) currentView);

// Get two buttons to the bottom

buttonOne.setSize(100, 30);

buttonOne.setLocation(10, 100);

buttonTwo.setSize(100, 30);

buttonTwo.setLocation(130, 100);

add(buttonOne);

add(buttonTwo);

}

publicvoidsetUpModelAndObservers() {

theModel = newSimpleModel();

theModel.addObserver((Observer) view1);

theModel.addObserver((Observer) view2);

}

publicvoidregisterListeners() {

upDateButton.addActionListener(newUpdateButtonListener());

ChangeViewListener listener = newChangeViewListener();

buttonOne.addActionListener(listener);

buttonTwo.addActionListener(listener);

}

// This is a controller in MVC. In general, controllers are implemented in

// Java as a registered listener to a components in the view.

// Notice that this controller interacts with the model.

privateclassUpdateButtonListenerimplementsActionListener {

publicvoidactionPerformed(ActionEventae) {

theModel.increment();

}

}

// Thislistener is needed to swap views and update the GUI to

// show the new view. With the current project, you have one

// view, a class like this is not needed.

privateclassChangeViewListenerimplementsActionListener {

publicvoidactionPerformed(ActionEventae) {

// cp is the object in the JFrame to which add and remove messages

// may be sent. Container cp = getContentPane();

cp.remove(currentView);

JButtonbuttonJustClicked = (JButton) ae.getSource();

if (buttonJustClicked == buttonOne)

currentView = view1;

if (buttonJustClicked == buttonTwo)

currentView = view2;

cp.add(currentView);

// We need both of these refreshes whenever switching views

// because the contentPane (cp) doesn't do it automatically and

// the repaint messages in the views is not in play at this second.

cp.validate();

cp.repaint();

}

}

}