The separation of concerns between CalculatorEngine and CalculatorInterface allows for the former to be reused in different environments. To show the ease of code reusability when a neat modular structure is adopted, another user-interface framework to work with CalculatorEngine is introduced in this section.
Similar to CalculatorInput, CalculatorFrame provides an environment for a CalculatorEngine object to execute. The major difference is that CalculatorFrame caters for a windowing environment, and gives the illusion that the calculator “hides”
behind the frame.
It demonstrates the context of a test harness and how it is easily constructed to aid incremental development.
It shows the synergistic cooperation of two objects with distinct concerns in an object-oriented design environment.
Implementation in Java 47 Windowing facilities in Java will be discussed in Chapter 13. However, this graphical calculator example is still appropriate since its objective is to show the benefits of modular code and reusable API libraries in Java. Code in the constructor method sets up a calculator frame with buttons and a display at appropriate locations.
Using a graphical user interface in this instance is fairly straightforward since mouse clicks on calculator buttons are mapped to actionPerformed() method. As such, code that performs the necessary dispatching to CalculatorEngine shown in List- ing 4-4 is similar to that in the run() method in CalculatorInput.
import java.awt.*;
import java.awt.event.*;
class CalculatorFrame extends Frame implements ActionListener { CalculatorEngine engine;
TextField display;
WindowListener listener = new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); } };
CalculatorFrame(CalculatorEngine e) { super("Calculator");
Panel top, bottom; Button b;
engine = e;
top = new Panel();
top.add(display = new TextField(20));
bottom = new Panel();
bottom.setLayout(new GridLayout(4,4));
bottom.add(b = new Button("1")); b.addActionListener(this);
bottom.add(b = new Button("2")); b.addActionListener(this);
bottom.add(b = new Button("3")); b.addActionListener(this);
bottom.add(b = new Button("+")); b.addActionListener(this);
bottom.add(b = new Button("4")); b.addActionListener(this);
bottom.add(b = new Button("5")); b.addActionListener(this);
bottom.add(b = new Button("6")); b.addActionListener(this);
bottom.add(b = new Button("-")); b.addActionListener(this);
bottom.add(b = new Button("7")); b.addActionListener(this);
bottom.add(b = new Button("8")); b.addActionListener(this);
bottom.add(b = new Button("9")); b.addActionListener(this);
bottom.add(b = new Button("*")); b.addActionListener(this);
bottom.add(b = new Button("C")); b.addActionListener(this);
bottom.add(b = new Button("0")); b.addActionListener(this);
bottom.add(b = new Button("=")); b.addActionListener(this);
bottom.add(b = new Button("/")); b.addActionListener(this);
setLayout(new BorderLayout());
add("North", top);
add("South", bottom) ;
addWindowListener(listener) ; setSize(180, 160) ;
show();
}
public void actionPerformed(ActionEvent e) { char c = e.getActionCommand().charAt(0);
if (c == '+') engine.add();
else if (c == '-') engine.subtract();
else if (c == '*') engine.multiply();
48 Object-Oriented Programming and Java
Listing 4-4: CalculatorFrame class.
4.4.1 Event-Driven Programming
While much code may be presently skipped, it is of great encouragement to readers that the API libraries allow for windowing applications to be developed with mini- mal user-code. Much code occur in the constructor and actionPerformed() meth- ods, which sets up the calculator buttons and respond to mouse clicks.
The code in the CalculatorFrame class looks somewhat strange because it is not completely procedural in its specification. In particular, while the body of the actionPerformed() method resembles that in run()in CalculatorInput, the for- mer is not explicitly invoked from within the class, such as from static void main() (as was the case for CalculatorInput) .
Procedural programming is the paradigm where actions are specified in a step- by-step sequence such as a baking recipe. Within each Java method, code is specified procedurally and the execution order may be easily determined.
In event-driven programming, code fragments are instead associated with events and invoked when these events occur. In a typical graphical environment with windowing facilities, events correspond to mouse movements, mouse clicks and keystrokes from the keyboard. It is impossible to determine or plan in advance what course of actions users might take to accomplish a certain task. Instead, we associate code fragments with significant events so that their side-effects will be appropriate response to such external events.
In an object-oriented system, methods are convenient units of code, and are used to receive stimuli from external devices. In Java, an ActionListener keeps a lookout for events involving mouse clicks. This is relevant to our CalculatorFrame, and we in turn implement the actionPerformed() method as a trigger point for mouse clicks on calculator buttons.
Thus, for each push as a calculator button, actionPerformed() is invoked, and it uses the getActionCommand() to identify which button. The framework for win- dowing using the AWT API will be further elaborated in Chapter 13.
Similarly, WindowAdaptor is used to monitor events involving windows, and windowClosing() is the corresponding method which is invoked when the user clicks to close the calculator window. Execution is terminated via System.exit(0).
else if (c == '/') engine.divide();
else if (c >= '0' && c <= '9') engine.digit(c - '0');
else if (c == '=') engine.compute();
else if (c == 'C') engine.clear();
display.setText(new Integer(engine.display()).toString());
}
public static void main(String arg[]) { new CalculatorFrame(new CalculatorEngine());
} }
Implementation in Java 49