/** * * * The emphasis on this question is to decouple TempController from * TempModel. In part (a) of the question, this decoupling was achieved * by moving all action code into a subclass. In part (b) of the question, * this decoupling was to be achieved through delegation: * *
* * Subclassing is one way of separating TempModel from * TempController. In the original TheremometerMVC318 and your variant * in part (a), there is always a direct reference to a TempModel object * within the TempController hierarchy. Thus setting the temperature is * never delegated to another object. Modify the TempController of * ThermometerMVC318 so that it delegates the temperature modification * action to another object. Aftwards, neither TempController nor any of * its subclasses should be setting the temperature directly. * * (Hint: Your delegate object could implement the ActionListener * interface to respond to change requests.) *
* * As in part (a), there are different ways of achieving this goal. The * essential part is that the separation occurs through some delegation * mechanism. There are several candidates for this delegation, but all * of them rely on a delegate reference being remembered by TempController * or one of the objects it is composed of: * * (1) implement delegation through the standard Java delegation * interface of java.util.Observable/Observer. Then TempController * would be a subclass of Observable and you would pass in an * Observer. Then the actionPerformed() implementation in * TempController would delegate the action call by calling * notifyObservers(). * * (2) implement delegation directly using your own implementation * instead of inheriting it from java.util.Observable. * * (3) ignore the Observable/Observer stuff and just pass in an * ActionListener to TempController so that the button events * are not even handled in the TempController. This is the * solution given here, but note that this does not separate * the TempController (which is UI code) from the rest of the * classes (which must therefore add UI code). The solution is * not therefore the most modular solution, but fills the minimum * modularization objective for TempController. */ import java.awt.*; import java.awt.event.*; import java.util.*; public class q1b extends java.applet.Applet { // --> change 1: // tempModel was moved to the class scope to emphasize that it // remains active throughout and updated through a TempController // child object. // final TempModel tempModel = new TempModel(); // <-- end change 1 public void init() { final TempView tempView = new TempView(); // -> change 2: // tempController is initialized by giving it the delegate object // (listenDelegate) final ActionListener listenDelegate = new ActionListener() { public void actionPerformed ( ActionEvent e ) { tempModel.setTemperature ((int)(Math.random()*tempModel.MAX_TEMP_F) ); } }; final TempController tempController = new TempController ( "Switch temperature (0-"+tempModel.MAX_TEMP_F+"F)", listenDelegate ); // <-- end change 2 tempModel.addObserver ( tempView ); // Decorate { setForeground ( Color.black ); // Static panel to display model info // final TextArea ta = new TextArea ( tempModel.toString(), 5, 40 ); ta.setEditable ( false ); final Panel infoPanel = new Panel(); infoPanel.setLayout ( new GridLayout(2,1) ); infoPanel.add ( new Label("Model") ); infoPanel.add ( ta ); setLayout ( new GridLayout(4,1) ); add ( new Label("ThermometerMVC318: Illustrating the Model-View-Controller in Java")); add ( infoPanel ); } add ( tempView.getPanel() ); add ( tempController.getPanel() ); } } class TempModel extends Observable { public static int MAX_TEMP_F = 250; public static int MIN_TEMP_F = 0; private int fahrenheit; private int celcius; public TempModel() { this(0); } public TempModel ( final int temp ) { setTemperature ( temp ); } final public void setTemperature ( int newTempF ) { if ( newTempF > MAX_TEMP_F ) { newTempF = MAX_TEMP_F; } else if ( newTempF < MIN_TEMP_F ) { newTempF = MIN_TEMP_F; } this.fahrenheit = newTempF; this.celcius = (int) ((double)(fahrenheit-32)*5/9); setChanged(); notifyObservers(); } public int F() { return fahrenheit; } public int C() { return celcius; } public int K() { return celcius+273; } public String toString() { return "TempModel: provides current temp\n"+ "in fahrenheit, celcius, or Kelvin.\n"+ "Max temp = "+MAX_TEMP_F+"F; Min temp ="+MIN_TEMP_F+"F\n"+ "setTemperature(int) expects fahrenheit.\n"; } } class TempView implements Observer { private final Label myLabel; private final Panel viewPanel; public TempView() { myLabel = new Label ( "View" ); myLabel.setText ( "Current temperature: " ); myLabel.setForeground ( Color.red ); viewPanel = new Panel(); viewPanel.setLayout ( new GridLayout(2,1) ); viewPanel.add ( new Label ( "View" ) ); viewPanel.add ( myLabel ); } public Panel getPanel() { return viewPanel; } public void update ( Observable ob, Object arg ) { TempModel temp = (TempModel) ob; myLabel.setText ( "Temperature: "+temp.F()+"F, "+ temp.C()+"C, "+temp.K()+"K"); } } class TempController { final private Button myButton; final private Panel myPanel; public Panel getPanel() { return myPanel; } // change 3: tempModel is removed // --> change 4 // TempController now takes the label text and the delegate object. // This delegate is passed on to the event handler for myButton // (which uses the AWT 1.1 *delegation-based* event model). // public TempController ( String labelText, ActionListener delegate ) // <-- end change 4 { myButton = new Button(); myButton.setForeground ( Color.blue ); myButton.setLabel ( labelText ); // change 4.1: passed label myPanel = new Panel(); myPanel.setLayout ( new GridLayout(2,1) ); myPanel.add ( new Label("Controller") ); myPanel.add ( myButton ); myButton.addActionListener ( delegate ); // change 4.2: registered } }