Hatena::Groupbeautiful-program

fkmの日記

2009-08-01

第0-1回

02:09

プラットフォームを選ばないという理由でJava swingで書いてみた


import java.util.Stack;
import java.util.Map;
import java.util.HashMap;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JTextField;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class CalcForm extends JFrame{
    // サイズなどの定数
    private static final String FORM_NAME = "簡易電卓";
    private static final int FORM_HEIGHT = 600;
    private static final int FORM_WIDTH = 600;

    public CalcForm(){
	super(FORM_NAME);
	setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	setBounds(10, 10, FORM_WIDTH, FORM_HEIGHT);

        // UI部分生成
        setUI();
    }

    private void setUI(){
	//数値表示用
	JPanel textPanel = new JPanel();
	textPanel.setPreferredSize(new Dimension(FORM_WIDTH, 60));
	JTextField textField = new JTextField();
	textField.setPreferredSize(new Dimension(FORM_WIDTH - 10, 50));
	//編集は不可にする
	textField.setEditable(false);
	textField.setFont(new Font(Font.SERIF, Font.PLAIN, 40));
	textField.setHorizontalAlignment(JTextField.RIGHT);

	textField.setText("0");

	textPanel.add(textField);
	getContentPane().add(textPanel, BorderLayout.NORTH);

	//計算エンジン
	CalcEngine engine = new CalcEngine(textField);
	
        JPanel panel = new JPanel();
	//レイアウトはグリッド
	panel.setLayout(new GridLayout(4, 4));
	
	//ボタンの作成
	for(int y = 0 ; y < NumberedButton.BUTTON_LABELS.length ; ++y){
	    for(int x = 0 ; x < NumberedButton.BUTTON_LABELS[y].length ; ++x){
		NumberedButton button = new NumberedButton(engine, x, y);
		button.addMouseListener(button);
		panel.add(button);
	    }
	}
        getContentPane().add(panel, BorderLayout.CENTER);

    }
}

class NumberedButton extends JButton implements MouseListener{
    private int x;
    private int y;
    private CalcEngine engine;

    public static final String[][] BUTTON_LABELS = new String[][]{
	{"7", "8", "9", "/"},
	{"4", "5", "6", "*"},
	{"1", "2", "3", "-"},
	{"0", "=", "C", "+"},
    };
    public NumberedButton(CalcEngine engine, int x, int y){
	super(BUTTON_LABELS[y][x]);
	this.x = x;
	this.y = y;
	this.engine = engine;
    }

    public void mouseEntered(MouseEvent e){ /**/ }

    public void mouseExited(MouseEvent e){ /**/ }

    public void mouseReleased(MouseEvent e){ /**/ }

    public void mousePressed(MouseEvent e){
	engine.addValue(BUTTON_LABELS[y][x]);	
    }
    public void mouseClicked(MouseEvent e){ /**/ }
    
}

class CalcEngine{
    private JTextField display;

    //計算用スタック
    private Stack<String> calStack = new Stack<String>();

    //次回の数値セット時に表示部分をクリアするフラグ
    private boolean clearDispay = false;

    //オペレータとそれに対応したメソッドのマップ
    private static Map<String, Calculable> operatorMap = new HashMap<String, Calculable>();
    static{
	operatorMap.put("+", new Plus());
	operatorMap.put("-", new Minus());
	operatorMap.put("*", new Multiple());
	operatorMap.put("/", new Divide());
    }

    /**
     * 引数が演算子か調べる
     */
    private boolean isOperator(String value){
	return operatorMap.containsKey(value);
    }
    
    public CalcEngine(JTextField disp){
	this.display = disp;
    }

    /**
     * UIより数値を受け取るメソッド
     */
    public void addValue(String value){
	String nowValue = display.getText();

	if(isOperator(value)){
	    if(!calStack.empty()){
		calc();
		nowValue = display.getText();
	    }
	    //演算子だったらスタックに積む
	    calStack.push(nowValue);
	    calStack.push(value);
	    //次数字押した時は画面消去
	    clearDispay = true;
	}else if(value.equals("=")){
	    //計算
	    calc();
	}else if(value.equals("C")){
	    //全クリア
	    calStack.clear();
	    display.setText("0");
	}else{
	    if(clearDispay){
		clearDispay = false;
		nowValue = "";
	    }
	    //先頭が0だったらその0は消しておく
	    if(nowValue.equals("0")){
		nowValue = "";
	    }
	    //表示の限界は8文字
	    if(nowValue.length() < 8){
		display.setText(nowValue + value);
	    }
	}
    }

    /**
     * 実際に計算を行います
     */
    private void calc(){
	// 計算スタックが空の時は何もしない
	if(calStack.empty()){
	    return;
	}
	String operator = calStack.pop();
	int leftVal = 0;
	int rightVal = 0;
	//右辺と左辺を数値化
	try{
	    String leftValString = calStack.pop();
	    leftVal = Integer.parseInt(leftValString);
	    String rightValString = display.getText();
	    rightVal = Integer.parseInt(rightValString);
	}catch(NumberFormatException e){
	    return;
	}

	//実際に計算は演算子クラスがやってくれる
	Calculable op = operatorMap.get(operator);
	String result = op.calc(leftVal, rightVal);

	//結果を表示	
	display.setText(result);
    }
}

/**
 * 演算できることを表すインターフェース
 */
interface Calculable{
    String calc(int leftValue, int rightValue);
}

/**
 * 足し算を行う演算子
 */
class Plus implements Calculable{
    public String calc(int leftValue, int rightValue){
	return leftValue + rightValue + "";
    }
}

/**
 * 引き算を行う演算子
 */
class Minus implements Calculable{
    public String calc(int leftValue, int rightValue){
	return leftValue - rightValue + "";
    }
}

/**
 * 掛算を行う演算子
 */
class Multiple implements Calculable{
    public String calc(int leftValue, int rightValue){
	return leftValue * rightValue + "";
    }
}

/**
 * 割り算を行う演算子
 */
class Divide implements Calculable{
    public String calc(int leftValue, int rightValue){
	if(rightValue == 0){
	    return "0";
	}
	return leftValue / rightValue + "";
    }
}

なかなか最初から意識して書かないと、ダメだなぁ

fkmfkm2009/08/02 02:19備忘録
・GridLayoutの引数はボタンラベルの配列の長さを使うべき
・setUIをボタンの初期化部分とディスプレイの初期化部分に分割すべき
・NumberedButtonの存在意義がいまひとつ

fumokmmfumokmm2009/08/02 02:59素敵なコードですね~。
最近私、Javaでこういったカッチリしたコード書いてないなぁ…。
fkmさんのこれを見習ってJavaでちゃんと書いてみるべきかなぁ。