Java 计算器的实现(两种不同思路)

这几天java课上老师要我们实现一个计算器。由于刚开始学习java,其中界面显示部分的代码老师已经准备好了,并且整个程序是采用MVCModel–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);
	}
}

(2)计算器图形版 程序入口

// 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);
	}
}

(3)计算器命令行版 程序入口

// 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));
		}
	}
}


==========================================

二、采用状态机(State Machine)思路编写的计算器核心模型(Calculator.java)

       基于从老师课上演示得到的灵感,感觉整个计算器的行为表现得就像一个状态机(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 );
	}
}


上面还需要补充一点,为了方便进行左、右操作数的运算, 操作数单独提出来作为一种OperaNum类来实现,另写为OperaNum.java如下:

/* 操作数用类来实现,统一各种运算
 --> 与数字有关的所有符号:【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+")";
	}
}


三、用简单的if语句控制的计算器核心模型(Calculator.java)

换一种思路,你说要简单来看嘛,可以把整个运行就分作3种状态:输入第1个操作数(INPUT_1)、输入运算符(INPUT_OP)、输入第二个操作数(INPUT_2)、已经输出了结果(SHOW)。这可以在Calculator类的开头定义出几个状态来用:

	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_1时,若按下了运算符,则进入INPUT_OP状态;

        在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);
	}
}






  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值