/** * * * The emphasis on this question is to decouple TempController from * TempModel. In part (a) of the question, the way to make this * decoupling was to move all references to what action events should * do to a subclass: * *
* TempController uses an anonymous inner class to set the model's * temperature through its tempModel reference. It is possible to remove * the need for tempModel in the code for TempController by putting all * direct references to tempModel only within subclasses of * TempController. That way only subclasses need to know about * TempModel. Modify the ThermometerMVC318 applet so that TempController * is separated from TempModel through subclassing. Keep the * myButton.addActionListener() ( new ActionListener() ... ); call within * TempController. *

* (Hint: you may need an abstract method in TempController). * (Hint #2: you may wish to use inner classes when subclassing * TempController) *

* * The solution is to make TempController abstract and do the actual action * through an abstract method, which would be overridden in the subclass to * do the actual temperature setting action. Obviously the subclass would * have to have access to the particular model being used. This could be * set up in at least two obvious ways: * * (1) make a regular subclass and pass the model object into the subclass * in its constructor. Then the overridden method will use this * reference to set the model. * * (2) make the subclass an inner class of the outer Applet child class. * Then the model could be accessed "directly" from the outer class * without having to remember a reference directly. That is the * solution given here. The other method is a straightforward * change of the subclassing, and is left as an exercise. */ import java.awt.*; import java.awt.event.*; import java.util.*; public class q1a 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: // make an anonymous inner class that can refer to // tempModel directly. Override specialization points // from the now-abstract TempController. // final TempController tempController = new TempController() { protected void doAction() { tempModel.setTemperature ( (int)(Math.random()*tempModel.MAX_TEMP_F) ); } protected void setLabel() { myButton.setLabel ( "Switch temperature (0-"+tempModel.MAX_TEMP_F+"F)" ); } }; // <-- 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"); } } abstract class TempController // change 3: made abstract { final protected Button myButton; final private Panel myPanel; public Panel getPanel() { return myPanel; } // --> change 4: // added abstract methods doAction() and setLabel(). These are // specialized by anyone wanting to make TempController do something // protected abstract void doAction(); protected abstract void setLabel(); // <-- end change 4 public TempController() { myButton = new Button(); myButton.setForeground ( Color.blue ); setLabel(); myPanel = new Panel(); myPanel.setLayout ( new GridLayout(2,1) ); myPanel.add ( new Label("Controller") ); myPanel.add ( myButton ); myButton.addActionListener ( new ActionListener() { public void actionPerformed (ActionEvent e) { doAction(); // change 5: made child class handle action } } ); } }