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();
}
}
}