面向对象程序设计 Guangzhou University 1 An object-based program is more interesting when multiple objects are involved. In addition to acting as peers or agents, objects can also serve as components in other objects, and building a class with component objects can enhance encapsulation, flexibility, and modifiability. A pocket calculator simulation program is used to illustrate this points. Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 2 Program design Applying object-based programming for our simulation, we employ object found in an actual calculator : A manager object (Calculator) that employs a compute engine component and a user interface component to perform the simulation. A user interface object (CalcFace) that reports user input to the Calculator and display results. A compute object( CalcEng) that supplies computing abilities and stores data. Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 3 The CalcEng class The calculator engine object performs the compute-methods: The method setOperand( 设置运算数 ) is used to enter numeric data into the compute engine ( ). The method operate is called to enter the next opcode and triger engine operations( 当具有两个 操作数时,触发计算功能,call compute). The method getOpcode is called to obtain the internally kept operator code(+, -,* so on). The method getOutput is invoked to produce the argument currently stored. Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 4 The calculator compute engine works with three fundamental quantities: The field ans is the answer, or result of requested computations, initialized to 0.0. The field op is the operation code (or opcode), one of the characters in the constant KEYS string, whose left operand is always ans. The field arg is the right operand of op. The field argcnt is used to store the number of operand. Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 5 Pocket Calculator Simulation 开始开始 ans = arg = 0.0; argcnt = 1; op = '='; setOperand operategetOutput 结束 cal.setOperand(9.8); ans=9.8; args=0.0; op= ‘ = ‘ ; argcnt=1 cal. operate( ‘ + ’ ); ans=9.8; args=0.0; op= ‘ + ‘ ; argcnt=1 cal.setOperand(1.2); ans=9.8; args=1.2; op= ‘ + ‘ ; argcnt=2 cal. operate( ‘ / ’ ); ans=11; args=1.2; op= ‘ / ‘ ; argcnt=1 cal.setOperand(2); cal.setOperate( ‘ = ’ ); ans=11; args=2; op= ‘ / ‘ ; argcnt=2 ans=5.5; args=2; op= ‘ = ‘ ; argcnt=1 ans=ans op args Argcnt=2 开始计算 结果存入 ans,argcnt=1
面向对象程序设计 Guangzhou University 6 /////// CalcEng.java /////// class CalcEng { // public members public CalcEng() { allClear(); } public String keys() { return KEYS; } public byte precision() { return PREC; } public char getOpcode() { // returns current opcode return op; } // 如果有两个操作时, 表示表达式还没计算, 返回右操作数, public double getOutput(){ // returns current argument return( argcnt==2 ? arg : ans ); } Pocket Calculator Simulation 读取计算器支持的操符 : "+-*/=NAC ” 读取计算器支持的数据精度 :8
面向对象程序设计 Guangzhou University 7 public void operate(char nc) { // nc is next opcode switch( nc ){ case 'A': allClear(); return; // All Clear case 'C': clear(); return; // Clear case 'N': // sign change if ( argcnt == 1 ) ans = -ans; else arg = -arg; return; default : // +-*/= compute(); op = nc; // new opcode } Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 8 /// private members private void compute(){ if ( argcnt == 2 ){ switch( op ){ case '+': ans += arg; break; case '-': ans -= arg; break; case '*': ans *= arg; break; case '/': ans /= arg; break; } argcnt = 1; } Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 9 private void clear(){ if ( argcnt == 1 ) { ans = 0.0; op = '='; }else{ arg = 0.0; argcnt = 1; } public void setOperand(double in) { if ( op == '=' ) ans = in; else{ arg = in; argcnt = 2; } Pocket Calculator Simulation 如果操作符目前为 =, 则计算结果直接为 in 否则, 将输入值赋给右操作数, 并计录此时 已有两个操作数了. 清计算器时, 若此时只一个操作数, 则彻底清空 否则, 只清右操作数, 并计此时只一个操作数
面向对象程序设计 Guangzhou University 10 private void allClear(){ ans = arg = 0.0; argcnt = 1; op = '='; } private final String KEYS = "+-*/=NAC"; private final byte PREC = 8; //digit number private double ans, arg; private char op; // operation code private int argcnt; // argument count } Pocket Calculator Simulation // 为了调式方便, 而增加的, 实际使用时可 使删除 public static void main(String[] args){ CalcEng cal = new CalcEng(); cal.setOperand(9.8); cal.operate('+'); cal.setOperand(1.2); cal.operate('/'); cal.setOperand(2.0); cal.operate('='); System.out.println(cal.getOutput()); }
面向对象程序设计 Guangzhou University 11 /////// CalcFace.java /////// import java.io.*; class CalcFace { public CalcFace(String k, byte pr){ keys = k; // 记录支持的 op prec = pr++; // 记录支持的精度 nbuf = new StringBuffer(prec); // 缓冲区 reset(); } public void setCalc(Calculator ca){ calc = ca; } public void showNumber(String s) { System.out.println(prompt + s); } public void showOp(char op) { // 暂为空 } Pocket Calculator Simulation CalcFace have three public interface setCalc: used by Calculator Intput: used by Calculator showNumber; used by Calculator showOp; 该对象的作用是负责读取输入的字符, 并将结果返回给 Calculator
面向对象程序设计 Guangzhou University 12 public void input() throws IOException { int i; while ( (i= inchar()) != -1 ){ enter((char) i); } private void enter(char c) { if ( keys.indexOf(c) != -1 ){ // if c an operator showOp(c); if ( num ) calc.enterNumber(extractNumber(), c); else calc.enterOp(c); reset(); }else if ( nump(c) && nbuf.length() < prec ) { num = true; buildNumber(c); // 精度为 prec 位的数 } Pocket Calculator Simulation Tthe input method runs in a continuous loop, entering each input character, until the standard input is closed( 即输入 Ctrl-z 退出计算 ) The enter method processes each input character. If the Character is not an opcode, it is treated as part of an Input number. 如果当前为 operator, 若 num=true( 即前面为数字 ) , 则从 buffer 中提取数字.否则输入 opcode 如果当前不为 operator ,则看是否超过充许精度. 若超过丢弃超过部分. private int inchar() throws IOException { return System.in.read(); } private String prompt="Calc: "; private Calculator calc; private String keys; // keys recognized private StringBuffer nbuf; // buffer for input number private boolean num = false
面向对象程序设计 Guangzhou University 13 private boolean nump(char c){ return( c == '.' || Character.isDigit(c) ); } private String extractNumber() { return (nbuf.length() == 0) ? “0”: nbuf.toString(); // 将 StringBuffer 转换成 String 对象 } private void buildNumber(char c){ int i = nbuf.length(); if ( i == 0 && c == '0') return; // ignore leading zeros if ( c == '.' ) { // at most one decimal point if ( ! before_point ) return; else before_point = false; } nbuf.append(c); } Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 14 private void reset(){ before_point = true; nbuf.setLength(0); num = false; } private int inchar() throws IOException { return System.in.read(); } private String prompt="Calc: "; private Calculator calc; private String keys; // keys recognized private StringBuffer nbuf; // buffer for input number private byte prec; // max no of chars displayable private boolean before_point = true; private boolean num = false; } Pocket Calculator Simulation
面向对象程序设计 Guangzhou University 15 /////// Calculator.java /////// public class Calculator { public Calculator(CalcEng e, CalcFace f){ eng = e; cf = f; f.setCalc(this); } public void on() throws java.io.IOException { output(); cf.input(); } public void enterNumber(String number, char op){ eng.setOperand( Double.parseDouble(number) ); enterOp(op); } Pocket Calculator Simulation 进入 CalcFace Object 的 input 方法后, 即进入一个死循环 被 CalcFace 对象调用 private void output(){ double number = eng.getOutput(); cf.showNumber(""+number); }
面向对象程序设计 Guangzhou University 16 public void enterOp( char op ){ eng.operate( op ); output(); } private void output(){ double number = eng.getOutput(); cf.showNumber(""+number); } private CalcEng eng = null; private CalcFace cf = null; } Pocket Calculator Simulation 输入 opcode 时,若附合条件: argcn=2, 则进行计算 被 CalcFace 对象调用
面向对象程序设计 Guangzhou University 17 /////// RunCalc.java /////// public class RunCalc { public static void main(String[] args) throws java.io.IOException { CalcEng e = new CalcEng(); CalcFace f = new CalcFace(e.keys(), e.precision()); Calculator x = new Calculator(e, f); x.on(); return; } Pocket Calculator Simulation 启动 Calculator public void on() throws java.io.IOException { output(); cf.input(); }