1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 | // File : calc-ui-model/CalcLogic.java - The logic of a calculator.
// Description: This is the logic/model of a calculator.
// It has no user interface, but could be called from
// either a GUI or console user interface.
// Separating the model (logic) from the interface has advantages.
// In this program the model is small, so it may not be as obvious,
// but in larger programs the advantages can be substantial.
// 1. It is simpler for the developer to work with.
// 2. It can be used with many kinds of interfaces without changes. Eg,
// a GUI interface, a command-line interface, or a web-based interface.
// 3. The model can be changed (eg, to work with BigInteger) without
// changing the user interface. Of course, some changes require
// interface changes, but the separation makes this easier.
//
// Author : Fred Swartz - 2004-11-17 + 2007-02-13 - Placed in public domain.
// Possible enhancements:
// * Change numbers to double, or BigInteger, or even Roman numerals!
// This should be possible without the user interface knowing much
// about the change (except perhaps to add a "." for floating-point input).
// * Add error checking (eg, division by zero checking.
// How would you communicate an error to the caller? Ans: Exceptions.
// * Additional operations - change sign, mod, square root, ...
//////////////////////////////////////////////////////////////// class CalcLogic
public class CalcLogic {
//-- Instance variables.
private int _currentTotal; // The current total is all we need to remember.
/** Constructor */
public CalcLogic() {
_currentTotal = 0;
}
public String getTotalString() {
return "" + _currentTotal;
}
public void setTotal(String n) {
_currentTotal = convertToNumber(n);
}
public void add(String n) {
_currentTotal += convertToNumber(n);
}
public void subtract(String n) {
_currentTotal -= convertToNumber(n);
}
public void multiply(String n) {
_currentTotal *= convertToNumber(n);
}
public void divide(String n) {
_currentTotal /= convertToNumber(n);
}
private int convertToNumber(String n) {
return Integer.parseInt(n);
} Java: Example - Simple Calculator
| Here is the source for the simple calculator shown at the left. It's divided into two source files. - Main and GUI (Calc.java) - This is implemented as a subclass of JFrame, containing a small main program. It both builds the interface the user sees (the "view), and handles the events coming from the buttons (the "controller").
- Model (CalcLogic.java) - This is where the actual calculations take place. Altho this simple example doesn't show the full power of separating the business logic (often called the "model") from the user interface, there are many advantages in larger programs.
- It is simpler for the developer to work with.
- It can be used with many kinds of interfaces without changes. Eg, a GUI interface, a command-line interface, or a web-based interface.
- The model can be changed (eg, to work with BigInteger) without changing the user interface. Of course, some changes may require interface changes, but the separation makes this easier.
|
The main program
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211 | // calc-ui-model/Calc.java -- Fred Swartz
// Level : Intermediate.
// Structure : Three files: main, GUI (subclass of JFrame), logic.
// Components: JButton, JTextField (right justified).
// Containers: JFrame, several JPanels.
// Layouts : BorderLayout to put the other panels together.
// Two GridLayout panels for the buttons.
// Listeners : One ActionListener which is shared by all
// numeric key buttons. Similarly share
// an ActionListener for all operator buttons.
// ActionListener for Clear button.
// Other : Use Font to enlarge font for components.
// : try...catch for NumberFormatExceptions.
// Possible enhancements:
// Check for zero before division.
// Additional operations: mod, square root, sign change, ...
// Make this work with doubles, BigInteger, or ...
// Format double results with DecimalFormat
// Add keyboard listener.
// Change to RPN (Reverse Polish Notation)
/** calc-ui-model/CalcGUI.java - A GUI for the calculator.
* @author Fred Swartz
* @version 2004-04-20 Rodenbach, 2007-02-11 minor changes.
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
///////////////////////////////////////////////////////////////////// class Calc
class Calc extends JFrame {
//================================================================ constants
private static final Font BIGGER_FONT = new Font("monspaced", Font.PLAIN, 20);
//=================================================================== fields
//... Component referenced during execution
private JTextField _displayField; // display result / input.
//... Variables representing state of the calculator
private boolean _startNumber = true; // true: num key next
private String _previousOp = "="; // previous operation
private CalcLogic _logic = new CalcLogic(); // The internal calculator.
//============================================================== method main
public static void main(String[] args) {
//... Set the Look and Feel to that of system we're running on.
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception unused) {
; // Ignore exception because we can't do anything. Will use default.
}
//... Create the window.
Calc window = new Calc();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
//============================================================== constructor
public Calc() {
//... Set attributes of the display field
_displayField = new JTextField("0", 12);
_displayField.setHorizontalAlignment(JTextField.RIGHT);
_displayField.setFont(BIGGER_FONT);
//... Create and set attributes of clear button
JButton clearButton = new JButton("Clear");
clearButton.setFont(BIGGER_FONT);
clearButton.addActionListener(new ClearListener());
//... Use one listener for all numeric keys.
ActionListener numListener = new NumListener();
//... Layout numeric keys in a grid. Generate the buttons
// in a loop from the chars in a string.
String buttonOrder = "789456123 0 ";
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(5, 3, 2, 2));
for (int i = 0; i < buttonOrder.length(); i++) {
String keyTop = buttonOrder.substring(i, i+1);
JButton b = new JButton(keyTop);
if (keyTop.equals(" ")) {
//... Put a dummy button in this position.
b.setEnabled(false);
} else {
//... Put a digit button in the interface.
b.addActionListener(numListener);
b.setFont(BIGGER_FONT);
}
buttonPanel.add(b);
}
//... One ActionListener to use for all operator buttons.
ActionListener opListener = new OpListener();
//... Create panel with gridlayout to hold operator buttons.
// Use array of button names to create buttons in a loop.
JPanel opPanel = new JPanel();
opPanel.setLayout(new GridLayout(5, 1, 2, 2));
String[] opOrder = {"+", "-", "*", "/", "="};
for (int i = 0; i < opOrder.length; i++) {
JButton b = new JButton(opOrder[i]);
b.addActionListener(opListener);
b.setFont(BIGGER_FONT);
opPanel.add(b);
}
//... Put Clear button in flow layout to keep from expanding.
JPanel clearPanel = new JPanel();
clearPanel.setLayout(new FlowLayout());
clearPanel.add(clearButton);
//... Layout the top-level content panel.
JPanel content = new JPanel();
content.setLayout(new BorderLayout(5, 5));
content.add(_displayField, BorderLayout.NORTH );
content.add(buttonPanel , BorderLayout.CENTER);
content.add(opPanel , BorderLayout.EAST );
content.add(clearPanel , BorderLayout.SOUTH );
content.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//... Finish building the window (JFrame)
this.setContentPane(content);
this.pack();
this.setTitle("Simple Calc");
this.setResizable(false);
this.setLocationRelativeTo(null);
}//end constructor
//============================================================== actionClear
/** Called by Clear btn action listener and elsewhere.*/
private void actionClear() {
_startNumber = true; // Expecting number, not op.
_displayField.setText("0");
_previousOp = "=";
_logic.setTotal("0");
}
//////////////////////////////////////////// inner listener class OpListener
/** Listener for all op buttons. */
class OpListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// The calculator is always in one of two states.
// 1. A number must be entered -- an operator is wrong.
// 2. An operator must be entered.
if (_startNumber) { // Error: needed number, not operator
//... In this state we're expecting a number, but got an operator.
actionClear();
_displayField.setText("ERROR - No operator");
} else {
//... We're expecting an operator.
_startNumber = true; // Next thing must be a number
try {
// Get value from display field, convert, do prev op
// If this is the first op, _previousOp will be =.
String displayText = _displayField.getText();
if (_previousOp.equals("=")) {
_logic.setTotal(displayText);
} else if (_previousOp.equals("+")) {
_logic.add(displayText);
} else if (_previousOp.equals("-")) {
_logic.subtract(displayText);
} else if (_previousOp.equals("*")) {
_logic.multiply(displayText);
} else if (_previousOp.equals("/")) {
_logic.divide(displayText);
}
_displayField.setText("" + _logic.getTotalString());
} catch (NumberFormatException ex) {
actionClear();
_displayField.setText("Error");
}
//... set _previousOp for the next operator.
_previousOp = e.getActionCommand();
}//endif _startNumber
}//endmethod
}//end class
//////////////////////////////////// inner listener class ClearListener
/** Action listener for numeric keys */
class NumListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String digit = e.getActionCommand(); // Get text from button
if (_startNumber) {
//... This is the first digit, clear field and set
_displayField.setText(digit);
_startNumber = false;
} else {
//... Add this digit to the end of the display field
_displayField.setText(_displayField.getText() + digit);
}
}
}
//////////////////////////////////// inner listener class ClearListener
class ClearListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
actionClear();
}
}
} |
The logic/model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 | // File : calc-ui-model/CalcLogic.java - The logic of a calculator.
// Description: This is the logic/model of a calculator.
// It has no user interface, but could be called from
// either a GUI or console user interface.
// Separating the model (logic) from the interface has advantages.
// In this program the model is small, so it may not be as obvious,
// but in larger programs the advantages can be substantial.
// 1. It is simpler for the developer to work with.
// 2. It can be used with many kinds of interfaces without changes. Eg,
// a GUI interface, a command-line interface, or a web-based interface.
// 3. The model can be changed (eg, to work with BigInteger) without
// changing the user interface. Of course, some changes require
// interface changes, but the separation makes this easier.
//
// Author : Fred Swartz - 2004-11-17 + 2007-02-13 - Placed in public domain.
// Possible enhancements:
// * Change numbers to double, or BigInteger, or even Roman numerals!
// This should be possible without the user interface knowing much
// about the change (except perhaps to add a "." for floating-point input).
// * Add error checking (eg, division by zero checking.
// How would you communicate an error to the caller? Ans: Exceptions.
// * Additional operations - change sign, mod, square root, ...
//////////////////////////////////////////////////////////////// class CalcLogic
public class CalcLogic {
//-- Instance variables.
private int _currentTotal; // The current total is all we need to remember.
/** Constructor */
public CalcLogic() {
_currentTotal = 0;
}
public String getTotalString() {
return "" + _currentTotal;
}
public void setTotal(String n) {
_currentTotal = convertToNumber(n);
}
public void add(String n) {
_currentTotal += convertToNumber(n);
}
public void subtract(String n) {
_currentTotal -= convertToNumber(n);
}
public void multiply(String n) {
_currentTotal *= convertToNumber(n);
}
public void divide(String n) {
_currentTotal /= convertToNumber(n);
}
private int convertToNumber(String n) {
return Integer.parseInt(n);
}
} |
} |
No comments:
Post a Comment