1. 程式人生 > >2016012049小學四則運算練習軟件項目報告

2016012049小學四則運算練習軟件項目報告

寫入 設計文檔 over reporting analysis spl 小學 測試過程 分開

Coding.net倉庫地址:https://coding.net/u/zhh1011/p/QuestionMaker/git

克隆地址:https://git.coding.net/zhh1011/QuestionMaker.git

測試步驟:

1.進入src文件夾

2.在命令行輸入javac -encoding utf-8 Main.java

3.回車再輸入java Main 20

4.回車,將會在根目錄下(與src同級)產生result.txt

  說在前面:本篇博文用於提交本人課程作業,若讀者有興趣也可訪問https://edu.cnblogs.com/campus/nenu/2016SE_NENU/homework/1656了解作業要求。

一、需求分析

  從題目中的基本要求如下:

    1、輸入n,能輸出n個四則運算題目在與main文件同目錄的“result.txt”文件中;

    2、輸出的題目中,數字在0~100之間,運算符在3~5個之間;

    3、輸出的題目需含有答案,且答案不得包含負數與分數;

    4、題目在運算中不得包含負數與分數;

二、功能設計

  能夠根據用戶輸入的參數n隨機產生n道符合要求的練習題,自動算出答案,並將式子與答案以文檔的形式呈現。

三、設計實現

  在我的src文件下共有兩個包以及Main類:

技術分享圖片

  首先描述一下Main類:

  Main類的main方法:

  技術分享圖片

  以及用於遞歸來規範用戶輸入的inPut方法:

技術分享圖片

  conifg包下放著靜態的數據如學號,姓名等,以及自定義的用於限制用戶輸入的異常類,並不復雜,在這裏就不加贅述了。

  main包下放著entity包與worker包:

  技術分享圖片

  entity包內放著題目的實體類Question類

技術分享圖片

  在這裏我要展示一下toString()方法:

//規範化輸出題目字符串
    @Override
    public String toString() {
        Object[] box = new Object[nums*2+4];
        box[nums*2+1] = ‘=‘;
        box[nums*2+2] = results;
        box[nums
*2+3] = "\r\n"; for(int i = 0; i < nums*2+1;i=i+2){ box[i] = number[i/2]; } for(int i = 1; i < nums*2;i=i+2){ box[i] = chars[i/2]; } return Arrays.toString(box).replace(‘[‘,‘ ‘).replace(‘]‘,‘ ‘).replace(‘,‘,‘ ‘); }

  worker包內有這三個類

  技術分享圖片

  Maker類用於制造完整的題目並調用Tester類的對象對題目進行測試(並在測試過程中獲取答案):

  在這裏就簡單的展示一下它的結構(因為真的沒啥說的)

技術分享圖片

  Tester類是整個項目裏相對來講最復雜的模塊,我會放在算法詳解部分解釋運算部分,在這裏介紹我用到的構建方式:

  下面是Test類的數據域、構造器以及方法:

技術分享圖片

  以及運算模塊(我在這裏練習了一下Java裏的“策略模式”)

技術分享圖片

  最後是Outer類:

技術分享圖片

  最後在Outer的構造器裏直接完成輸出:



//Outer構造器
public Outer(int times){
    this.times = times;
    this.boxs = new String[times+1];
    Question question;
    boxs[0] = ID;
    for (int  i = 1;i <= times;){
        maker.makeQuestion();
        maker.testQuestion();
        if(!maker.getQuestion().isUseful())
            continue;
        String box = maker.getQuestion().toString();
        boxs[i] = box;
        i++;
    }
    //在這裏寫入"result.txt"文件
    outQuestion(boxs);
}

四、算法實現

  算法很簡單(甚至可以說沒有),只是運用java自己的鏈表(ArrayList與List)自帶的方法和遞歸完成簡單的運算,以及if-else判斷算符的優先級,使用策略模式優化代碼結構,並沒有使用所謂的調度場算法或者其他一些算法來實現。

 五、測試運行

  首先編譯源文件(需轉換為UTF-8進行編碼,否則會中文報錯),然後進行非法輸入測試:

  技術分享圖片

  進行正確輸入後檢查輸出是否正確:

技術分享圖片

六、展示部分代碼

  我本次項目不像其他人或擁有高超的算法或實現了更多的功能,我最滿意的地方在於自己在這次項目裏對Java程序設計的一種實踐。通過繼承、組合,用很多包、類,甚至采用了策略設計模式去模塊化自己的代碼,更簡易的去修改去改進,添加更多的註釋去幫助別人理解自己的代碼並進行再開發。

  舉個例子比如有關運算部分的代碼,使用了策略設計模式去模塊化運算操作:

//這一部分屬於Test類中的方法-----------------------------------------
//策略模式的方法
private Box function(Function f, Box box){
    return f.run(box);
}

//計算題目的答案
private int getResult(Box box){
    //用於判斷優先級的部分
    if(box.listC.indexOf(‘*‘)>box.listC.indexOf(‘÷‘))
        box = function(new Multiplication(),box);
    else if(!(box.listC.indexOf(‘÷‘)==(-1)))
        box = function(new Division(),box);
    else if(box.listC.get(0) == ‘+‘)
        box = function(new Plus(),box);
    else if(box.listC.get(0) == ‘-‘){
        box = function(new Subtraction(),box);
    }
    //實現遞歸的部分
    if(box.listC.isEmpty()){
        if(box.listN.get(0)==-1)
            questionTest.setUseful(false);
        return box.listN.get(0);
    }
    else {
        return getResult(box);
    }
}
//這一部分屬於Test類中的方法-----------------------------------------
//策略部分開始--------------------------------------------|
class Function{
    Box run(Box box){
        return box;
    }
}

//乘法運算部分
class Multiplication extends Function{
    @Override
    Box run(Box box){
        box.listN.set(box.listC.indexOf(‘*‘),box.listN.get(box.listC.indexOf(‘*‘))*box.listN.get(box.listC.indexOf(‘*‘)+1));
        box.listN.remove(box.listC.indexOf(‘*‘)+1);
        box.listC.remove(box.listC.indexOf(‘*‘));
        return box;
    }
}

//除法運算部分
class Division extends Function{
    @Override
    Box run(Box box) {
        if(!(box.listN.get(box.listC.indexOf(‘÷‘)+1)==0)&&box.listN.get(box.listC.indexOf(‘÷‘))%box.listN.get(box.listC.indexOf(‘÷‘)+1) == 0){
            box.listN.set(box.listC.indexOf(‘÷‘),box.listN.get(box.listC.indexOf(‘÷‘))/box.listN.get(box.listC.indexOf(‘÷‘)+1));
            box.listN.remove(box.listC.indexOf(‘÷‘)+1);
            box.listC.remove(box.listC.indexOf(‘÷‘));
        }
        else{
            //答案非法,直接停止遞歸
            box.listC.clear();
            box.listN.set(0,-1);
            return box;
        }
        return box;
    }
}

//加法部分
class Plus extends Function{
    @Override
    Box run(Box box) {
        box.listN.set(box.listC.indexOf(‘+‘),box.listN.get(box.listC.indexOf(‘+‘))+box.listN.get(box.listC.indexOf(‘+‘)+1));
        box.listN.remove(box.listC.indexOf(‘+‘)+1);
        box.listC.remove(box.listC.indexOf(‘+‘));
        return box;
    }
}

//減法運算部分
class Subtraction extends Function{
    @Override
    Box run(Box box) {
        if(box.listN.get(box.listC.indexOf(‘-‘))-box.listN.get(box.listC.indexOf(‘-‘)+1) > 0) {
            box.listN.set(box.listC.indexOf(‘-‘), box.listN.get(box.listC.indexOf(‘-‘)) - box.listN.get(box.listC.indexOf(‘-‘) + 1));
            box.listN.remove(box.listC.indexOf(‘-‘) + 1);
            box.listC.remove(box.listC.indexOf(‘-‘));
        }
        else {
            //答案非法,直接停止遞歸
            box.listC.clear();
            box.listN.set(0,-1);
            return box;
        }
        return box;
    }
}
//策略部分結束----------------------------------------|

//封裝數字與運算符的盒子(Box...)
class Box{
    List<Integer> listN = new ArrayList<>();
    List<Character> listC = new ArrayList<>();

    Box(List<Integer> listN,List<Character> listC){
        this.listC = listC;
        this.listN = listN;
    }
}

  

這些都讓我感覺更...更舒適,不去直觀的面對真正的實現與細節,而是通過組合,繼承,創建一個對象去調用它的方法去實現功能。讓我自己思路清晰,明白自己需要什麽,明白自己要幹什麽。我可以隨時完成一個小模塊的實現,也可以在日常的各種活動中,去思考一個小模塊如何去實現,利用起碎片化的時間,甚至有時候靈機一動解決一個模塊裏的小問題。在後期修改錯誤階段也可以很簡單就改變一個模塊的行為,因為它並不會牽扯很多東西,只需要修改調用它的模塊就行了,它處理的數值它內部的變化絲毫不會影響其他東西。

七、總結

 為了讓自己的代碼模塊化,首先我給問題建立了實體類來封裝有關問題的信息(包含的數字,字符,字符數量等等),隨後將功能模塊化,生成與測試分離,輸出與生成分離。且有關問題字符串形式的輸出也是在Question類的toString()方法中實現的。輸出(Outer)模塊僅僅是負責調用生成模塊生成符合的問題對象並規範輸出,生成模塊(Maker)也只是負責隨機生成題目對象然後調用測試模塊去測試並同時生成答案,測試(Tester)模塊內部Tester類自己只負責測試,運算部分由整個Function類及其子類完成。

PSP2.1

任務內容

計劃共完成需要的時間(h)

實際完成需要的時間(h)

Planning

計劃

1.5

3

· Estimate

· 估計這個任務需要多少時間,並規劃大致工作步驟

1.5

3

Development

開發

20

22

· Analysis

· 需求分析 (包括學習新技術)

2

3

· Design Spec

· 生成設計文檔

0

0.5

· Design Review

· 設計復審 (和同事審核設計文檔)

0

0.5

· Coding Standard

· 代碼規範 (為目前的開發制定合適的規範)

0

0

· Design

· 具體設計

2

3

· Coding

· 具體編碼

15

11

· Code Review

· 代碼復審

0

1

· Test

· 測試(自我測試,修改代碼,提交修改)

1

4

Reporting

報告

3

4

· Test Report

· 測試報告

0.5

0

· Size Measurement

· 計算工作量

0.5

0.5

· Postmortem & Process Improvement Plan

· 事後總結, 並提出過程改進計劃

2

3.5

  最令我意外的當屬開發最後的測試階段了,錯誤層出不斷,許多地方和自己想象的不一樣。對很多東西進行了大改,對最初的架構做了很大的改變(最初的架構可以在我之前博客:http://www.cnblogs.com/zanghh/p/8604724.html),設計和現實的差距真的很大,只有在運行和測試的時候才能發現(測試並沒有很規矩的去單元測試,只是寫了一些很簡單的方法自己去看輸出是否符合預期,相關代碼可以在test文件下看到)。其次就是計劃階段,斷斷續續一直在想那些,以為一個小時就能解決的東西,大概想了有兩三個小時,只能怪自己一直想著如何優化了(實際最後都沒去優化)。總的來說這次的實踐讓我受益匪淺,有機會去實踐一些自己以前學到的一些設計方式和思想,也看了很多新的設計理念。感受一項目從無到有的痛苦與愉悅(寫不出來的痛苦、項目完成的那一刻的激動真的無語言表...後來的測試又是當頭一棒...)。真的是學會了很多復習了很多實踐了很多(甚至打開了有半年多沒看過的API文檔),如果你是一名後來者,我真誠的建議你去自己敲一個從無到有的項目,並按正常的流程開發,絕對是一次不可多的體驗(特別是第一次的話),最後也希望如果你也這樣做得話,也請把自己的經驗也寫成一篇博客與人分享,讓這種體驗傳播的更為廣泛。

2016012049小學四則運算練習軟件項目報告