16進電卓を作ってみよう6

   2012/05/08

プログラムを作る前に考えた事(仕様設計)と
プログラムを動かしてみて感じることの間にはギャップがあるのが普通です。

毎度、当ブログ管理人のイガジーです。

仕様をしっかり決めてから作り始めましょう。
という様な話がよくある(昔はあった、今は?)のですが
私イガジーとしては、それは無理だと思うのです。

もちろん、何も考えずに作り始めるのは無謀ですが
全てを設計時(仕様決めの時点)に洗い出せる天才は少ないと思います。
(規模が小さいもの、あるいは経験のあるものであれば
不可能ではないですけれど)

ですから、ざっと決めた仕様を元にプロトタイピング(試作)して
仕様を見直して、またプロトタイプを作り、仕様に問題はないか
を考える、を繰り返す「スパイラル」開発が現実的です。

という事で、昨日の演算処理を組み込んでみたソース例と
その動作から仕様を考えてみましょう。

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;


public class Dentaku3 {
	Frame f0;
	Label dsp;
	Font fbig,fmid;
	Button btn[]=new Button[20];
	Checkbox hx,dc;
	CheckboxGroup cbg;

	final int NOP=0, ADD=1, SUB=2;
	int result=0,val1=0;
	int radix=10;
	int stat=NOP;

	final int KCLR=-1, KPLS=-2, KMNS=-3, KEQU=-4;
	String lb[]={
		"A","B","C","D",
		"7","8","9","E",
		"4","5","6","F",
		"1","2","3","CLR",
		"0","+","-","="
	};

	int fn[]={
			10,11,12,13,
			 7, 8, 9,14,
			 4, 5, 6,15,
			 1, 2, 3,KCLR,
			 0,KPLS,KMNS,KEQU
		};
	Dentaku3(){
		f0=new Frame();
		f0.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent ev) {
                System.exit(0);
            }
        });
		f0.setTitle("decHEX3");
		fbig=new Font("Monospaced",Font.PLAIN,36);
		fmid=new Font("Monospaced",Font.PLAIN,24);

		dsp=new Label("0");
		dsp.setAlignment(Label.RIGHT);
		dsp.setFont(fbig);
		dsp.setBackground(Color.LIGHT_GRAY);

		Panel p1=new Panel();
		p1.setLayout(new BorderLayout());

		cbg=new CheckboxGroup();
		dc=new Checkbox("dec",true,cbg);
		hx=new Checkbox("HEX",false,cbg);
		Panel prd=new Panel();
		prd.setLayout(new FlowLayout(FlowLayout.LEFT));
		prd.add(dc); prd.add(hx);
		p1.add(prd,BorderLayout.SOUTH);
		p1.add(dsp,BorderLayout.CENTER);

		Panel keys=new Panel();
		keys.setLayout(new GridLayout(5,4));
		for (int i=0;i<20;++i) {
			if (fn[i]>=0) btn[i]=new nBtn(lb[i],fn[i]);
			else {
				btn[i]=new Button(lb[i]);
				switch (fn[i]) {
				case KCLR:
					btn[i].addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent e) {
							result=0;
							display();
						}
					});
					break;
				case KPLS:
					btn[i].addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent e) {
							val1=result;
							stat=ADD;
							result=0;
							display("+");
						}
					});
					break;
				case KMNS:
					btn[i].addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent e) {
							val1=result;
							stat=SUB;
							result=0;
							display("-");
						}
					});
					break;
				case KEQU:
					btn[i].addActionListener(new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent e) {
							if (stat==ADD) {
								result+=val1;
							}else if (stat==SUB) {
								result=val1-result;
							}
							stat=NOP;
							display();
							// result=0;
						}
					});
					break;
				default:
						break;
				}
			}
			btn[i].setFont(fmid);
			keys.add(btn[i]);
		}
		hx.addItemListener(new setRadix());
		dc.addItemListener(new setRadix());

		hkeytop();
		f0.add(p1,BorderLayout.NORTH);
		f0.add(keys,BorderLayout.CENTER);
		f0.setSize(300,300);
		f0.setVisible(true);
	}
	public static void main(String[] args) {
		new Dentaku3();
	}
	void display(){
		String rs;
		if (radix==10)	rs=Integer.toString(result);
		else rs=Integer.toHexString(result).toUpperCase();
		dsp.setText(rs);
	}
	void display(String s){
		dsp.setText(s);
	}
	void hkeytop(){
		for (int k=0;k<20;++k){
			if (fn[k]>=10) {
				if (radix==16) btn[k].setForeground(Color.BLACK);
				else btn[k].setForeground(Color.LIGHT_GRAY);
			}
		}
	}
	class nBtn extends Button implements ActionListener {
		private static final long serialVersionUID = 1L;
		int nn;
		nBtn(String lb,int v){
			super(lb);
			nn=v;
			this.addActionListener(this);
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			if ((radix==16)||(nn<10)) {
				result=result*radix+nn;
				display();
			}
		}
	}
	class setRadix implements ItemListener {
		@Override
		public void itemStateChanged(ItemEvent e) {
			if (hx.getState()) {
				radix=16;
			}else{
				radix=10;
			}
			hkeytop();
			display();
		}
	}
}

statに入れる値を final int でラベル化しています。
stat=1; より、stat=ADD; の方が意味が分かりやすいですから。

これを動かしてみましょう。
[1][+][2][=]
とキー(ボタン)を押すと 3 と表示されますね(お見事?)。

続けて、[4]を押すと、あらら、34 と表示されます。
普通の電卓では、(計算結果の3がクリアされて)4 が表示されるのに
ちょっと違う動きです。

resultは、数字キーを押すたびに、10倍(正確にはradix倍)して
押された数字を加算する処理をしているので、当然の動きなのです。

[=]を押して結果を見たら、次は[CLR]を押す。という仕様だ。
と言いはる事もできます。
でも、ちょっと使いにくいですね。

では、[=]の処理の最後に、result=0; を入れれば良いでしょうか?
やってみてください。

case KEQU:
	btn[i].addActionListener(new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (stat==ADD) {
				result+=val1;
			}else if (stat==SUB) {
				result=val1-result;
			}
			stat=NOP;
			display();
			result=0; //追加
		}
	});
break;

この変更を加えて動かしてみると
[1][+][2][=] … 3が表示される
続けて
[4] … 4 が表示される。狙い通り。
続けて
[+][7][=] … 11 が表示される。よしよし。ここで
○HEX … (16進に切り替えると) 0 が表示されてしまいます。

また
[4][+][7][=] … 11が表示 の状態から
[+][2][=] … 2が表示される(11+2の13が出て欲しい)

これは、困ります。計算結果を16進にしたい事はよくあるし、
続けての演算もしたいですよね。

あらためて変更前に戻すと
([=]キー処理の末尾に加えた result=0;を削除すると)

[4][+][7][=] … 11表示
○HEX … (16進に切り替えると)B が表示されます。
○dec … (10進に切り替えると) 11が表示されます。
これは、これで良い。

11が表示されている状態で、[+][2][=]とすると13になる。(OK)

でも、
[CLR]
[1][+][2][+][3][=] …5 が表示される。ちょっと期待と違いますね。

そして、新たに数字を入力する前には[CLR]を押さなければなりません。
それは不便です。

さて、困りました。どうしましょう?
こういうのは、動かしてみないとなかなか分からないものです。

だんだん処理が複雑になってきますが、頑張ってみてください。

16進電卓を作ってみよう1
16進電卓を作ってみよう2
16進電卓を作ってみよう3
16進電卓を作ってみよう4
16進電卓を作ってみよう5
16進電卓を作ってみよう6(this)
16進電卓を作ってみよう7
16進電卓を作ってみよう8
16進電卓を作ってみよう9

この記事へのコメントはこちら

メールアドレスは公開されませんのでご安心ください。
また、* が付いている欄は必須項目となりますので、必ずご記入をお願いします。

内容に問題なければ、下記の「コメント送信」ボタンを押してください。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)