CS 596 Java Programming Fall Semester, 1998 Model-View |
||
---|---|---|
© 1998, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 21-Dec-98 |
Separating the GUI from the Program
A Simple Example
Problem: create a simple counter that can:
A Poor Solution - The Window is the Counter!
import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; class BadCounterExample extends Frame { Button increase = new Button( "Increase" ); Button decrease = new Button( "Decrease" ); Button reset = new Button( "Reset" ); int count = 0; public BadCounterExample( int width, int height ) { setTitle( "Bad Counter Example" ); setSize( width, height ); setLayout( new FlowLayout() ); add( increase ); add( decrease ); add( reset ); increase.addActionListener( new IncreaseListener() ); decrease.addActionListener( new DecreaseListener() ); reset.addActionListener( new ResetListener() ); show(); } public void paint( Graphics display ) { display.drawString( "The count is " + count , 50, 50 ); } public static void main( String args[] ){ new BadCounterExample( 200, 100 ); } class IncreaseListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count++; repaint(); } } class DecreaseListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count--; repaint(); } } class ResetListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count = 0; repaint(); } } }
Output After clicking on Increase 6 times
What is Wrong with the about program?It works! What happens if we need to run the program in batch mode? There is no way to separate the code from window environment!
Model-View
A Model is a system of objects that perform a task
A View:
Displays a representation of the model by asking model for values to display
Handles all interaction with user about the model
Keeping the model and view separate
class Counter{ private int count = 0; public void increase() { count++; } public void decrease() { count--; } public void reset() { count = 0; } public int value() { return count; } public String toString() { return String.valueOf( count ); } }
A View for the Model
import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; class CounterExample extends Frame { Button increase = new Button( "Increase" ); Button decrease = new Button( "Decrease" ); Button reset = new Button( "Reset" ); Counter count; public CounterExample( int width, int height, Counter outside ) { setTitle( "Model-View Example" ); setSize( width, height ); setLayout( new FlowLayout() ); add( increase ); add( decrease ); add( reset ); count = outside; increase.addActionListener( new IncreaseListener() ); decrease.addActionListener( new DecreaseListener() ); reset.addActionListener( new ResetListener() ); show(); } public void paint( Graphics display ) { display.drawString( "The count is " + count , 50, 50 ); } class IncreaseListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count.increase(); repaint(); } } class DecreaseListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count.decrease(); repaint(); } } class ResetListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count.reset(); repaint(); } } }
Driver ProgramTwo Views Two Models
class ButtonTest { public static void main( String args[] ) { Counter test = new Counter(); new CounterExample( 200, 100, test ); test = new Counter(); new CounterExample( 200, 100, test ); } }
How about Two Views, One Model?
class ButtonTest { public static void main( String args[] ) { Counter test = new Counter(); CounterExample ViewA = new CounterExample( 200, 100, test ); CounterExample ViewB = new CounterExample( 200, 100, test ); } }
What Went Wrong?
When view A changes the counter how does view B know to update?
Who's responsibility is it to notify B of the change?
Observable and Observer
(Dependents)(Publish-Subscribe) The Observer is similar to the listeners we have seen before. The Observable is similar to the event sources for the listeners. The difference is in the functionality of the Observable class. Its event broadcast mechanism is more complex.
Class java.util.Observable
java.util.Observable Methods
Simple Example class SimpleCounter extends Observable{ public int count = 0; public void increase() { count++; setChanged(); notifyObservers(); } } class SimpleObserver implements Observer { String id; public SimpleObserver( String id ) { this.id = id ; } public void update( Observable sender, Object message ) { System.out.println( "From " + id + " New value " + ((SimpleCounter) sender).count ); } } class ObserveTest { public static void main( String args[] ) { SimpleCounter test = new SimpleCounter(); SimpleObserver a = new SimpleObserver( "a" ); test.addObserver( a); test.addObserver( new SimpleObserver( "b" ) ); test.increase(); test.addObserver( new SimpleObserver( "c" ) ); test.increase(); } }
Running the Example
class ObserveTest { public static void main( String args[] ) { SimpleCounter test = new SimpleCounter(); SimpleObserver a = new SimpleObserver( "a" ); test.addObserver( a); test.addObserver( new SimpleObserver( "b" ) ); test.increase(); test.addObserver( new SimpleObserver( "c" ) ); test.increase(); } }Output From b New value 1 From a New value 1 From c New value 2 From b New value 2 From a New value 2
Adding the Same Observer Twice?
class ObserveTest { public static void main( String args[] ) { SimpleCounter test = new SimpleCounter(); SimpleObserver a = new SimpleObserver( "a" ); test.addObserver( a); test.addObserver( a ); test.increase(); System.out.println( "After first increase"); test.addObserver( a ); test.increase(); } }Output From a New value 1 After first increase From a New value 2
Deleting Observers
class ObserveTest { public static void main( String args[] ) { SimpleCounter test = new SimpleCounter(); SimpleObserver a = new SimpleObserver( "a" ); SimpleObserver b = new SimpleObserver( "b" ); test.addObserver( a); test.addObserver( b); test.increase(); System.out.println( "Delete a" ); test.deleteObserver( a ); test.increase(); System.out.println( "Delete a again" ); test.deleteObserver( a ); test.increase(); System.out.println( "Delete all" ); test.deleteObservers( ); test.increase(); } }Output From b New value 1 From a New value 1 Delete a From b New value 2 Delete a again From b New value 3 Delete all
Counter Example
We will go through the complete counter example with the observable-observer system. Here is the full counter modified as an Observable (Model).
class Counter extends Observable { private int count = 0; public void increase() { count++; setChanged(); notifyObservers(); } public void decrease() { count--; setChanged(); notifyObservers(); } public void reset() { count = 0; setChanged(); notifyObservers(); } public int value() { return count; } public String toString() { return String.valueOf( count ); } }
A Counter Observer View import java.awt.*; import java.awt.event.*; import java.util.*; class CounterExample extends Frame implements Observer { Button increase = new Button( "Increase" ); Button decrease = new Button( "Decrease" ); Button reset = new Button( "Reset" ); Counter count; public CounterExample( int width, int height, Counter outside ) { setTitle( "Observer Example" ); setSize( width, height ); setLayout( new FlowLayout() ); add( increase ); add( decrease ); add( reset ); count = outside; count.addObserver( this ); increase.addActionListener( new IncreaseListener() ); decrease.addActionListener( new DecreaseListener() ); reset.addActionListener( new ResetListener() ); show(); } public void update( Observable sender, Object message ) { repaint(); } public void paint( Graphics display ) { display.drawString( "The count is " + count , 50, 50 ); } class IncreaseListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count.increase(); } } class DecreaseListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count.decrease(); } } class ResetListener implements ActionListener { public void actionPerformed( ActionEvent event ) { count.reset(); } } }
Running the Example class ModelViewTest{ public static void main( String args[] ){ Counter test = new Counter(); CounterExample ViewA = new CounterExample( 200, 100, test ); CounterExample ViewB = new CounterExample( 200, 100, test ); } }
Interaction Diagram
This diagram shows the method calls between ViewA, test and ViewB when the increase button of ViewA is pressed. Time progresses as you read from top to bottom.
Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.
visitors since 22-Nov-98