Coding.net倉庫地址:https://coding.net/u/zhh1011/p/QuestionMaker/githtml
克隆地址:https://git.coding.net/zhh1011/QuestionMaker.gitjava
測試步驟:git
1.進入src文件夾算法
2.在命令行輸入javac -encoding utf-8 Main.java設計模式
3.回車再輸入java Main 20架構
4.回車,將會在根目錄下(與src同級)產生result.txtide
說在前面:本篇博文用於提交本人課程做業,若讀者有興趣也可訪問https://edu.cnblogs.com/campus/nenu/2016SE_NENU/homework/1656瞭解做業要求。模塊化
1、需求分析單元測試
從題目中的基本要求以下:學習
一、輸入n,能輸出n個四則運算題目在與main文件同目錄的「result.txt」文件中;
二、輸出的題目中,數字在0~100之間,運算符在3~5個之間;
三、輸出的題目需含有答案,且答案不得包含負數與分數;
四、題目在運算中不得包含負數與分數;
2、功能設計
可以根據用戶輸入的參數n隨機產生n道符合要求的練習題,自動算出答案,並將式子與答案以文檔的形式呈現。
3、設計實現
在個人src文件下共有兩個包以及Main類:
首先描述一下Main類:
Main類的main方法:
public static void main(String[] args) { //用於規範用戶輸入的部分--------- try { NumberException exception = new NumberException(); times = Integer.valueOf(args[0]); times = exception.testNumber(times); }catch (Exception e){ System.out.println("請輸入1~1000的數字"); times = inPut(); } //--------------------------- Outer outer = new Outer(times); }
以及用於遞歸來規範用戶輸入的inPut方法:
//遞歸方法,用於規範用戶輸出 private static int inPut(){ try { //自定義的異常,用於判斷輸入是否處於1~1000 NumberException exception = new NumberException(); Scanner input = new Scanner(System.in); times = input.nextInt(); times = exception.testNumber(times); }catch (Exception e){ System.out.println("請輸入1~1000的數字"); inPut(); } return times; }
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);
}
4、算法實現
算法很簡單(甚至能夠說沒有),只是運用java本身的鏈表(ArrayList與List)自帶的方法和遞歸完成簡單的運算,以及if-else判斷算符的優先級,使用策略模式優化代碼結構,並無使用所謂的調度場算法或者其餘一些算法來實現。
5、測試運行
首先編譯源文件(需轉換爲UTF-8進行編碼,不然會中文報錯),而後進行非法輸入測試:
進行正確輸入後檢查輸出是否正確:
6、展現部分代碼
我本次項目不像其餘人或擁有高超的算法或實現了更多的功能,我最滿意的地方在於本身在此次項目裏對Java程序設計的一種實踐。經過繼承、組合,用不少包、類,甚至採用了策略設計模式去模塊化本身的代碼,更簡易的去修改去改進,添加更多的註釋去幫助別人理解本身的代碼並進行再開發。
舉個例子好比有關運算部分的代碼,使用了策略設計模式去模塊化運算操做:
1 //這一部分屬於Test類中的方法----------------------------------------- 2 //策略模式的方法
3 private Box function(Function f, Box box){ 4 return f.run(box); 5 } 6
7 //計算題目的答案
8 private int getResult(Box box){ 9 //用於判斷優先級的部分
10 if(box.listC.indexOf('*')>box.listC.indexOf('÷')) 11 box = function(new Multiplication(),box); 12 else if(!(box.listC.indexOf('÷')==(-1))) 13 box = function(new Division(),box); 14 else if(box.listC.get(0) == '+') 15 box = function(new Plus(),box); 16 else if(box.listC.get(0) == '-'){ 17 box = function(new Subtraction(),box); 18 } 19 //實現遞歸的部分
20 if(box.listC.isEmpty()){ 21 if(box.listN.get(0)==-1) 22 questionTest.setUseful(false); 23 return box.listN.get(0); 24 } 25 else { 26 return getResult(box); 27 } 28 } 29 //這一部分屬於Test類中的方法----------------------------------------- 30 //策略部分開始--------------------------------------------|
31 class Function{ 32 Box run(Box box){ 33 return box; 34 } 35 } 36
37 //乘法運算部分
38 class Multiplication extends Function{ 39 @Override 40 Box run(Box box){ 41 box.listN.set(box.listC.indexOf('*'),box.listN.get(box.listC.indexOf('*'))*box.listN.get(box.listC.indexOf('*')+1)); 42 box.listN.remove(box.listC.indexOf('*')+1); 43 box.listC.remove(box.listC.indexOf('*')); 44 return box; 45 } 46 } 47
48 //除法運算部分
49 class Division extends Function{ 50 @Override 51 Box run(Box box) { 52 if(!(box.listN.get(box.listC.indexOf('÷')+1)==0)&&box.listN.get(box.listC.indexOf('÷'))%box.listN.get(box.listC.indexOf('÷')+1) == 0){ 53 box.listN.set(box.listC.indexOf('÷'),box.listN.get(box.listC.indexOf('÷'))/box.listN.get(box.listC.indexOf('÷')+1)); 54 box.listN.remove(box.listC.indexOf('÷')+1); 55 box.listC.remove(box.listC.indexOf('÷')); 56 } 57 else{ 58 //答案非法,直接中止遞歸
59 box.listC.clear(); 60 box.listN.set(0,-1); 61 return box; 62 } 63 return box; 64 } 65 } 66
67 //加法部分
68 class Plus extends Function{ 69 @Override 70 Box run(Box box) { 71 box.listN.set(box.listC.indexOf('+'),box.listN.get(box.listC.indexOf('+'))+box.listN.get(box.listC.indexOf('+')+1)); 72 box.listN.remove(box.listC.indexOf('+')+1); 73 box.listC.remove(box.listC.indexOf('+')); 74 return box; 75 } 76 } 77
78 //減法運算部分
79 class Subtraction extends Function{ 80 @Override 81 Box run(Box box) { 82 if(box.listN.get(box.listC.indexOf('-'))-box.listN.get(box.listC.indexOf('-')+1) > 0) { 83 box.listN.set(box.listC.indexOf('-'), box.listN.get(box.listC.indexOf('-')) - box.listN.get(box.listC.indexOf('-') + 1)); 84 box.listN.remove(box.listC.indexOf('-') + 1); 85 box.listC.remove(box.listC.indexOf('-')); 86 } 87 else { 88 //答案非法,直接中止遞歸
89 box.listC.clear(); 90 box.listN.set(0,-1); 91 return box; 92 } 93 return box; 94 } 95 } 96 //策略部分結束----------------------------------------| 97
98 //封裝數字與運算符的盒子(Box...)
99 class Box{ 100 List<Integer> listN = new ArrayList<>(); 101 List<Character> listC = new ArrayList<>(); 102
103 Box(List<Integer> listN,List<Character> listC){ 104 this.listC = listC; 105 this.listN = listN; 106 } 107 }
這些都讓我感受更...更溫馨,不去直觀的面對真正的實現與細節,而是經過組合,繼承,建立一個對象去調用它的方法去實現功能。讓我本身思路清晰,明白本身須要什麼,明白本身要幹什麼。我能夠隨時完成一個小模塊的實現,也能夠在平常的各類活動中,去思考一個小模塊如何去實現,利用起碎片化的時間,甚至有時候靈機一動解決一個模塊裏的小問題。在後期修改錯誤階段也能夠很簡單就改變一個模塊的行爲,由於它並不會牽扯不少東西,只須要修改調用它的模塊就好了,它處理的數值它內部的變化絲絕不會影響其餘東西。
7、總結
爲了讓本身的代碼模塊化,首先我給問題創建了實體類來封裝有關問題的信息(包含的數字,字符,字符數量等等),隨後將功能模塊化,生成與測試分離,輸出與生成分離。且有關問題字符串形式的輸出也是在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文檔),若是你是一名後來者,我真誠的建議你去本身敲一個從無到有的項目,並按正常的流程開發,絕對是一次不可多的體驗(特別是第一次的話),最後也但願若是你也這樣作得話,也請把本身的經驗也寫成一篇博客與人分享,讓這種體驗傳播的更爲普遍。