赞
踩
这几天java课上老师要我们实现一个计算器。由于刚开始学习java,其中界面显示部分的代码老师已经准备好了,并且整个程序是采用MVC(Model–view–controller,点击打开链接 )的设计模式,我们要实现的只是其中的Model,即核心的算法模型。先看看用户界面(View部分)吧。
为了让大家方便试验计算器程序,现把计算器的实现代码发上来。下面的是老师发布题目的代码。
其中只有Calculator.java是MVC中的Model部分,这次也只需要修改这部分代码。其它部分提供了界面的布局、按钮等,不需要改变。
(1)最初的 Calculator类 定义
// Calculator.java
- // The core of the great calculator
- // Check the "TODO"s!!
-
- class Calculator {
- String expression = "0";
-
- // TODO: modify the method to return a proper expression
- // which will be shown in the screen of the calculator
- String getExpression() {
- return expression;
- }
-
- // TODO: modify the method to handle the key press event
- void keyPressed(char key) {
- expression += key;
- }
-
- // TODO: you can modify this method to print any debug
- // information (It will be called by CalculatorCmd)
- void debugPrintStatus() {
- System.out.println("Expression = " + expression);
- }
- }
// CalculatorApp.java
- // Define entry point of the calculator application
-
- class CalculatorApp {
- public static void main(String[] args) {
- CalculatorWindow mainwnd = new CalculatorWindow();
- CalculatorController control = new CalculatorController();
- mainwnd.setController(control);
- mainwnd.setVisible(true);
- }
- }
// CalculatorCmd.java
- // A calculator with command line interface
-
- import java.io.*;
-
- public class CalculatorCmd {
- public static void main(String[] args) throws IOException {
- System.out.println("Welcome to use commoand line calculator.");
-
- Calculator calculator = new Calculator();
-
- while (true) {
- calculator.debugPrintStatus();
- System.out.println("\nEXP = " + calculator.getExpression());
- System.out.print("input: ");
- char c = (char)System.in.read();
- if (c == 'x' || c == 'X') break;
- calculator.keyPressed(c);
- }
-
- System.out.println("\nBye.");
- }
- }
(4)控制器(Control)部分,将按下的 按键key 移交给keypressed(char key)函数,这也是计算器需要编写的重点。
// CalculatorController.java
- // The controller of the calculator application
-
- import java.awt.event.*;
- import javax.swing.*;
-
- class CalculatorController implements ActionListener {
-
- Calculator calc = new Calculator();
- JLabel window;
-
- public void actionPerformed(ActionEvent e) {
- char key = e.getActionCommand().charAt(0);
- calc.keyPressed(key);
- if (window!=null) {
- window.setText(calc.getExpression());
- }
- }
-
- void setDisplayWindow(JLabel w) {
- window = w;
- }
- }
(5)计算器的窗口布局,有点繁琐,暂时可以不用细究。
- // The window of the calculator application
- // Layout the buttons, and register the button action events
-
- import java.awt.*;
- import javax.swing.*;
- import javax.swing.border.*;
- import java.awt.event.*;
-
- class CalculatorWindow extends JFrame {
- JLabel disp;
- JButton cancelButton, equalButton, dotButton;
- JButton signButton, addButton, subButton, mulButton, divButton;
- JButton[] numButton;
-
- CalculatorWindow() {
- this.setSize(400, 440);
- this.setResizable(false);
- this.setTitle("Java Calculator");
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-
- Container wnd = getContentPane();
- wnd.setLayout(null);
-
- JPanel dispPanel = new JPanel();
- JPanel controlPanel = new JPanel();
- wnd.add(dispPanel);
- wnd.add(controlPanel);
- dispPanel.setBounds(0,0,400,60);
- controlPanel.setBounds(0,60,400,360);
-
- dispPanel.setBorder(new LineBorder(Color.GRAY));
- disp = new JLabel("0");
- // disp.setBorder(new LineBorder(Color.RED));
- disp.setSize(new Dimension(380, 60));
- Font dispFont = new Font("Arial", Font.PLAIN, 24);
- disp.setFont(dispFont);
- disp.setHorizontalAlignment(SwingConstants.RIGHT);
- dispPanel.setLayout(null);
- dispPanel.add(disp);
- // dispPanel.setMinimumSize(new Dimension(400, 500));
-
- GridBagLayout gridbag = new GridBagLayout();
- // controlPanel.setLayout(new GridLayout(4,4,10,20));
- controlPanel.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
- controlPanel.setLayout(gridbag);
- controlPanel.setBorder(new EmptyBorder(20,10,20,10));
-
- cancelButton = new JButton("c");
- signButton = new JButton("+/-");
- addButton = new JButton("+");
- subButton = new JButton("-");
- mulButton = new JButton("*");
- divButton = new JButton("/");
- equalButton = new JButton("=");
- numButton = new JButton[10];
- for(int i=0; i<10; i++) {
- numButton[i] = new JButton(String.valueOf(i));
- }
- dotButton = new JButton(".");
-
- GridBagConstraints c = new GridBagConstraints();
- c.insets = new Insets(3,2,3,2);
- c.fill = GridBagConstraints.BOTH;
- c.weightx = 1.0;
- c.weighty = 1.0;
- gridbag.setConstraints(mulButton, c);
- controlPanel.add(mulButton);
- gridbag.setConstraints(divButton, c);
- controlPanel.add(divButton);
- gridbag.setConstraints(signButton, c);
- controlPanel.add(signButton);
- c.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(cancelButton, c);
- controlPanel.add(cancelButton);
-
- c.gridwidth = 1;
- gridbag.setConstraints(subButton, c);
- controlPanel.add(subButton);
- gridbag.setConstraints(numButton[9], c);
- controlPanel.add(numButton[9]);
- gridbag.setConstraints(numButton[8], c);
- controlPanel.add(numButton[8]);
- c.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(numButton[7], c);
- controlPanel.add(numButton[7]);
-
- c.gridwidth = 1;
- gridbag.setConstraints(addButton, c);
- controlPanel.add(addButton);
- gridbag.setConstraints(numButton[6], c);
- controlPanel.add(numButton[6]);
- gridbag.setConstraints(numButton[5], c);
- controlPanel.add(numButton[5]);
- c.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(numButton[4], c);
- controlPanel.add(numButton[4]);
-
- c.gridwidth = 1;
- c.gridheight = 2;
- gridbag.setConstraints(equalButton, c);
- controlPanel.add(equalButton);
- c.gridheight = 1;
- gridbag.setConstraints(numButton[3], c);
- controlPanel.add(numButton[3]);
- gridbag.setConstraints(numButton[2], c);
- controlPanel.add(numButton[2]);
- c.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(numButton[1], c);
- controlPanel.add(numButton[1]);
-
- c.gridwidth = 1;
- gridbag.setConstraints(dotButton, c);
- controlPanel.add(dotButton);
- c.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(numButton[0], c);
- controlPanel.add(numButton[0]);
-
- this.addWindowListener(new WindowAdapter() {
- public void windowOpened(WindowEvent e) {
- equalButton.requestFocus();
- }
- });
- }
-
- class CalculatorHotKey extends KeyAdapter {
- public void keyPressed(KeyEvent e) {
- char key = e.getKeyChar();
- if (key >= '0' && key <= '9') {
- numButton[key - '0'].doClick();
- } else switch(e.getKeyChar()) {
- case 'c':
- cancelButton.doClick();
- break;
- case '~':
- signButton.doClick();
- break;
- case '+':
- addButton.doClick();
- break;
- case '-':
- subButton.doClick();
- break;
- case '*':
- mulButton.doClick();
- break;
- case '/':
- divButton.doClick();
- break;
- case '=':
- equalButton.doClick();
- break;
- case '.':
- dotButton.doClick();
- break;
- case '\n':
- equalButton.doClick();
- break;
- }
- }
-
- }
-
- void setController(CalculatorController control) {
- control.setDisplayWindow(disp);
-
- CalculatorHotKey keyMap = new CalculatorHotKey();
-
- cancelButton.addKeyListener(keyMap);
- cancelButton.addActionListener(control);
- cancelButton.setActionCommand("c");
-
- signButton.addKeyListener(keyMap);
- signButton.addActionListener(control);
- signButton.setActionCommand("~");
-
- addButton.addKeyListener(keyMap);
- addButton.addActionListener(control);
- addButton.setActionCommand("+");
-
- subButton.addKeyListener(keyMap);
- subButton.addActionListener(control);
- subButton.setActionCommand("-");
-
- mulButton.addKeyListener(keyMap);
- mulButton.addActionListener(control);
- mulButton.setActionCommand("*");
-
- divButton.addKeyListener(keyMap);
- divButton.addActionListener(control);
- divButton.setActionCommand("/");
-
- equalButton.addKeyListener(keyMap);
- equalButton.addActionListener(control);
- equalButton.setActionCommand("=");
-
- dotButton.addKeyListener(keyMap);
- dotButton.addActionListener(control);
- dotButton.setActionCommand(".");
-
- for(int i=0; i<10; i++) {
- numButton[i].addKeyListener(keyMap);
- numButton[i].addActionListener(control);
- numButton[i].setActionCommand(String.valueOf(i));
- }
- }
- }
==========================================
基于从老师课上演示得到的灵感,感觉整个计算器的行为表现得就像一个状态机(StateMachine)。上学期学EDA时,利用VHDL语言在FPGA硬件平台上完成许多任务时也是建立状态机模型来实现,我想那不妨就用状态机模型来指导整个程序的编写吧。我整理了一下计算器的各种状态,画了一张状态转移图。当然,或许还可以再设计出更简单的状态图。
就像图例所示,每个状态有各自的状态编号st(i),i=1,2,..,5;初始状态为复位(st0)。
每个状态根据键盘不同的输入而跳转到不同的下一个状态,在每个状态中执行相应的操作。如下图:
[1] 操作数置换:因为输入完第二操作数后,不是按等号,而是继续按下运算符,于是先把结果记作第1操作数,清空第二操作数,并继续进入输入运算符的状态。
在程序中,状态的编号是用枚举变量实现的,这个状态机的结构是在keyPressed(char key)函数中利用一个switch(c_state)实现的,其中c_state意味着当前状态(current state)。在每一个状态下会执行那个状态的服务函数fun_sti(),i=1,2,..,5;在这些函数内实现当前状态的任务以及状态跳转的控制。
我自己觉得用这种思路去写程序,容易方便程序的不断“升级壮大”,因为每次我只需要仔细地完成某个状态的代码便可开始试运行,就算是某个状态的代码出错了,还不会影响到其它状态的执行,容易找出错误位置。还容易依此添加新的控制能力,就比如说要控制往屏幕上输出消息,可以拿当前状态为依据输出不同的消息:
完整的实现代码如下:
// Calculator.java
- // The core of the great calculator
- // Check the "TODO"s!!
-
- class Calculator {
- private double result=0.0;
- private char[] symbols={'+','-','*','/'}; // 定义将会使用到的符号
- private char symbol_use=' '; // 最近一次运算被选中的符号,默认为空
- private enum STATE {st0, st1, st2, st3, st4, st5}; // 状态图中使用的状态
- private STATE c_state= STATE.st1; // 初始状态直接从 st1 开始好了
-
- private OperaNum op_left = new OperaNum(), // 定义左、右操作数
- op_right = new OperaNum();
-
- // 在屏幕上显示的内容,依据当前的不同状态来定。
- String getExpression() {
- switch(c_state){
- case st0:
- case st1:
- return op_left.STR_value();
- case st2:
- return op_left.STR_value() + symbol_use;
- case st3:
- return op_left.STR_value() + symbol_use + op_right.STR_value();
- case st4:
- return "= " + result;
- default:
- return "0";
- }
- }
-
- // 下面是处于各状态需要做的事情
- private void fun_st0(){
- op_left.setClear();
- op_right.setClear();
- result = 0.0;
- c_state = STATE.st1;
- }
-
- private void fun_st1_basic(char key){
- if( (key>='0' && key<='9') || key=='.' || key=='~' ){
- op_left.pushDigital(key);
- c_state = STATE.st1;
- }
- else if( key=='=' ){
- // result =
- symbol_use = '=';
- fun_result();
- c_state = STATE.st4;
- }
- else if( key=='c' ){ // 按下C键
- fun_st0();
- c_state = STATE.st0;
- }
- else { // 输入运算符号
- fun_st2(key);
- c_state = STATE.st2;
- }
- }
-
- private void fun_st2(char key){
- if( key=='+' || key=='-' || key=='*' || key=='/'){
- symbol_use = key;
- c_state = STATE.st2;
- }
- else if( (key>='0' && key<='9') || key=='.' || key=='~' ){
- fun_st3(key);
- c_state = STATE.st3;
- }
- }
-
- private void fun_st3(char key){
- if( (key>='0' && key<='9') || key=='.' || key=='~' ){
- op_right.pushDigital(key);
- c_state = STATE.st3;
- }
- else if( key=='=' ){
- // result =
- fun_result();
- c_state = STATE.st4;
- }
- else if( key=='c' ){ // 按下C键
- fun_st0();
- c_state = STATE.st0;
- }
- else{
- c_state = STATE.st5; // 按下运算符号
- fun_st5(key);
- }
- }
-
- void fun_st4(char key){ // 此时,已经显示出了结果
- if( (key>='0' && key<='9') || key=='.' || key=='~' ){
- fun_st0();
- op_left.pushDigital(key);
- c_state = STATE.st1;
- }
- else if( key=='+' || key=='-' || key=='*' || key=='/' ){ // 输入运算符号
- op_left.setValue(result);
- op_right.setClear();
- fun_st2(key);
- c_state = STATE.st2;
- }
- }
-
- void fun_st5(char key){
- fun_result();
- op_left.setValue(result);
- op_right.setClear();
- fun_st2(key);
- c_state = STATE.st2;
- }
-
- private void fun_result(){
- switch(symbol_use){
- case '+':
- result = op_left.value() + op_right.value();
- break;
- case '-':
- result = op_left.value() - op_right.value();
- break;
- case '*':
- result = op_left.value() * op_right.value();
- break;
- case '/':
- if( op_right.value()==0.0 )
- result = Double.POSITIVE_INFINITY;
- else
- result = op_left.value() / op_right.value();
- break;
- default: // 等号,或来自 st1 的直接按下等号
- result = op_left.value();
- break;
- }
- }
-
- // 在此函数中设定状态机模型
- void keyPressed(char key) {
- //expression += key;
- if(key=='c')
- c_state = STATE.st0; // 复位一下
-
- switch(c_state){
- case st0: fun_st0(); // 初始状态,并且等待输入
- case st1: fun_st1_basic(key); break; // 输入左操作数
- // ===============
- case st2: fun_st2(key); break;
- case st3: fun_st3(key); break;
- case st4: fun_st4(key); break;
- case st5: fun_st5(key); break;
- default:
- fun_st0();
- c_state = STATE.st0;
- break;
- }
- }
-
- // TODO: you can modify this method to print any debug
- // information (It will be called by CalculatorCmd)
- void debugPrintStatus() {
- // System.out.println("Expression = " + expression);
- System.out.println( "c_state" + c_state );
- }
- }
- /* 操作数用类来实现,统一各种运算
- --> 与数字有关的所有符号:【0-9】,【.】,【~】
- */
- public class OperaNum{
- String expression="0"; // 操作数的字符串表达式
- private double v=0.0; // 操作数的值
- private boolean SIGN=true; // true= pos, false= minus.
-
- OperaNum(double v){
- setValue(v);
- }
-
- OperaNum(){}
-
- void pushDigital(char key){ // 用于实现状态图中的“输入操作数”
- // (1) 输入普通数字
- if( key>='0' && key<='9')
- if( expression.equals("0") ){ // 若字符串是初始状态0,再按0无反应
- if( key=='0')
- expression="0";
- else
- expression = String.valueOf(key);
- }
- else
- expression +=key;
-
- if(key=='.') // (2) 处理小数点
- pushDot(key);
- if(key=='~') // (3) 处理符号
- SIGN = !SIGN;
-
- v= value();
- }
-
- void pushDot(char key){ // 帮助pushDigital()函数来输入小数点,这里要检查是否合乎规范
- if( expression.indexOf('.')<=0 ) // 若不成立,则表达式已经有小数点了
- expression += '.';
- }
-
- void setValue(double init_num){ // 强制设定操作数,一般不用
- v = init_num;
- expression = String.valueOf(v);
- }
-
- void setClear(){
- v = 0;
- expression = "0";
- SIGN = true;
- }
-
- double value(){ // 返回当前操作数的值,还要根据是否加入了负号来决定取值
- v= Double.parseDouble(expression);
- return SIGN? v : (-1)*v;
- }
-
- String STR_value(){ // 返回当前操作数的字符串表达式,
- return SIGN? expression : "(-"+expression+")";
- }
- }
- private final int INPUT_1 = 1;
- private final int INPUT_OP = 2;
- private final int INPUT_2 = 3;
- private final int SHOW = 4;
- private int INPUT_STATE = 1; // 存放程序当前所处状态
在INPUT_OP状态时若按下了数字则进入了INPUT_2状态;之后若按下了等号则进入SHOW状态,并再自动进入INPUT_1状态。
在INPUT_2状态时,若按下了运算符,则先把当前结果记作第1操作数,清空第2操作数,并进入INPUT_OP状态。
实现代码如下:
// Calculator.java
- // The core of the great calculator
- // Check the "TODO"s!!
-
- class MyNum{
- String expression="0"; // 操作数的字符串表达式
- private double value=0.0; // 操作数的值
- boolean SIGN=true; // 表示数字是正数还是负数
-
- MyNum(){}
-
- void pushDigital(char key){
- if( key>='0' && key<='9') // 输入数字
- if( expression.equals("0") ){ // 若字符串是初始状态0,再按0无反应
- if( key=='0')
- expression="0";
- else
- expression = String.valueOf(key);
- }
- else
- expression += key;
- else if(key=='.') // 小数点
- pushDot(key);
- else if(key=='~') // 正负数的转变
- SIGN = !SIGN;
- }
-
- void pushDot(char key){ // 如果已经输入过小数点了,就不再输入
- if( expression.indexOf('.')<0 )
- expression += '.';
- }
-
- void set(double num){ // 强制设定操作数,一般不用
- value = num;
- expression = String.valueOf(value);
- }
-
- void clear(){
- value = 0;
- expression = "0";
- SIGN = true;
- }
-
- double read_value(){ // 返回当前操作数的值,还要根据是否加入了负号来决定取值
- value = Double.parseDouble(expression);
- return SIGN? value : (-1)*value;
- }
-
- String get_exp(){ // 返回当前操作数的字符串表达式,
- return SIGN? expression : " -"+expression;
- }
- }
-
-
- class Calculator {
- String expression = "0";
- char operator=' ';
- double result=0.0;
- MyNum left_op = new MyNum(),
- right_op = new MyNum();
- private final int INPUT_1 = 1;
- private final int INPUT_OP = 2;
- private final int INPUT_2 = 3;
- private final int SHOW = 4;
- private int INPUT_STATE = 1; // 存放程序当前所处状态
-
- // TODO: modify the method to return a proper expression
- // which will be shown in the screen of the calculator
- String getExpression() {
- if(INPUT_STATE==INPUT_1){
- expression = left_op.get_exp();
- }
- else if(INPUT_STATE==INPUT_OP){
- expression = "" + left_op.get_exp() + operator;
- }
- else if(INPUT_STATE==INPUT_2){
- expression = "" + left_op.get_exp() + operator + right_op.get_exp();
- }
- else if(INPUT_STATE==SHOW)
- expression = "= " + result;
-
- return expression;
- }
-
- // TODO: modify the method to handle the key press event
- void keyPressed(char key) {
- // expression += key;
- if(key=='c'){
- left_op.clear();
- right_op.clear();
- expression = "0";
- operator=' ';
- INPUT_STATE = INPUT_1;
- }
-
- if( key>='0' && key<='9' || key=='.' || key=='~'){
- if (INPUT_STATE == SHOW) {
- left_op.clear();
- right_op.clear();
- expression = "0";
- operator=' ';
- INPUT_STATE = INPUT_1;
- left_op.pushDigital(key);
- }
- else if(INPUT_STATE==INPUT_1){
- left_op.pushDigital(key);
- }
- else if (INPUT_STATE==INPUT_OP){
- INPUT_STATE = INPUT_2;
- right_op.pushDigital(key);
- }
- else if (INPUT_STATE==INPUT_2) {
- right_op.pushDigital(key);
- }
- }
-
- if( key=='+' || key=='-' || key=='*' || key=='/' ){
- if(INPUT_STATE==INPUT_1)
- INPUT_STATE=INPUT_OP;
- else if(INPUT_STATE == SHOW){
- left_op.set(result);
- right_op.clear();
- INPUT_STATE=INPUT_OP;
- }
- // else if(INPUT_STATE)
- operator = key;
- }
-
- if(key=='='){
- result = get_result();
- INPUT_STATE = SHOW;
- }
-
- }
-
- double get_result(){
- switch(operator){
- case '+': return left_op.read_value() + right_op.read_value();
- case '-': return left_op.read_value() - right_op.read_value();
- case '*': return left_op.read_value() * right_op.read_value();
- case '/':
- if(right_op.read_value()==0) return Double.POSITIVE_INFINITY;
- else return left_op.read_value() / right_op.read_value();
- default: return left_op.read_value();
- }
-
- }
-
- // TODO: you can modify this method to print any debug
- // information (It will be called by CalculatorCmd)
- void debugPrintStatus() {
- System.out.println("Expression = " + expression);
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。