軟件工程 第三周


軟件工程第三周


1.項目成員

(我LJL是多余的那個,跟張敏老師商量之后成立了這個三人小組)
張翔:201521123107
李嘉廉:201521123091
林正晟:201521123084
結對編程碼雲地址:https://gitee.com/ljl36/pair_programming


2.需求分析

(1)現有代碼的改進
  ①啟用了錯誤歷史記錄;
  ②改正一些錯誤的編碼和用法;
  ③修改代碼樣式;
  ④修改掉了如果錯題集只有一道題,復習多道題時出現一模一樣的多道題的bug;
  ⑤修正了界面中“開始”按鈕可以重復點擊的問題;
  ⑥修改了計時框中的計時可以隨意更改的問題;
  ⑦發現並修改了界面左下角正確率顯示不正確的問題。
  
(2)新開發功能的分析
  ①加入了乘方和括號參與運算
  ②隨機產生更加復雜的表達式
  ③更加強大的去重算法


  

3.程序設計

(1)代碼規范
ⅰ.使用tab進行代碼縮進
ⅱ.斷行與空白的{}行
  ‘{’與if和for在同一行,操作符的兩邊各留一個空格,逗號和分號也各留一個空格,如下圖:


ⅲ.命名規范沿用原項目命名規則:

Arithmetic類
astr            返回答案字符串
qstr            返回問題字符串
int_operation()        整數計算函數
fra_operation()        分數計算函數
common_divisor(int m,int n)
公約數計算函數
toString()        輸出函數
Frame類
hs            實例化History
hh            實例化QA_List
timer            實例化Work_Time
Time            計時器動態窗口
answer1~10        輸入窗口
Review            復習按鈕
question1~10        題目顯示標簽
Right_answer1~10    正確答案顯示標簽
Tip1~10         提示正確與否標簽
Time_cost         所花時間顯示標簽
Right_percent        正確率顯示標簽
Set_question         開始按鈕
jLabel5            歷史正確題數
jLabel7            歷史總題數
rn            單次正確題數
tot            單次總題數
Frame()            Frame構造函數
initComponents()    窗體顯示
xxxClicked(KeyEvent evt)    
點擊事件
KeyPressed(JLabel i,JLabel j,JLabel l,int x,JTextField k)
點擊事件批量處理函數
Hide()            使不需要的輸入窗口關閉
Tip(String answer,int i)提示對錯並計入
Histroy類
qstr            問題字符串
astr            答案字符串
str            答案+問題字符串(寫入文件)
str2            正確題數+總題數字符串(寫入文件)
tot            總題數
rn            正確題數
qstrlist        問題字符串列(讀入文件)
astrlist        答案字符串列(讀入文件)
scan(String qstr,String astr)
qstr+" "+astr
scan2(int tot,int rn)
tot+" "+rn
Histroy_create()    歷史文檔生成
Histroy_save()        存儲歷史題目與答案
Histroy_saveNum()    存儲歷史題目數目
Histroy_read()        讀入歷史題目與答案
History_num()        讀入歷史題目數目
QA_List類
i            加入列題目個數
Qusetion        問題字符串列
Answer            答案字符串列
QA_List()        QA_List構造函數
Test_Number類
i            顯示Test_Number窗體次數
l            語言轉化參數標識
Cancel_Button        取消按鈕
Sure_Button        確定按鈕
English            英文界面標簽
Simplified_Chinese    簡體中文界面標簽
Traditional_Chinese    繁體中文界面標簽
Number            所需題數
Test_Number()        Test_Number構造函數
initComponents()    Test_Number窗體顯示
xxxClicked(KeyEvent evt)    
點擊事件
Test類
main(String[] args)    
Work_Time類
x            00:00.x
y            00:y.00
z            z:00.00

ⅳ.局部變量和方法按照駝峰風格命名,類名采用Pascal風格

(2)PSP表格

PSP2.1 Personal Software Process Stages Estimated time(min) Actual time(min)
Planning 計划 60 60
· Estimate 估計這個任務需要多少時間 60 60
Development 開發 720 687
· Analysis 需求分析 120 60
· Coding Standard 代碼規范 30 45
· Design 具體設計 90 72
· Coding 具體編碼 300 240
· Code Review 代碼復審 60 120
· Test 測試(自我測試,修改代碼,提交修改) 120 150
Reporting 報告 150 168
測試報告 60 60
計算工作量 30 48
並提出過程改進計划 60 60

(3)流程

(4)關鍵函數類圖

(5)單元測試、回歸測試、覆蓋率
①單元測試
  由於我們要進行單元測試的方法為私有方法,但
[深入JUnit] 為什么別測試private函數建議我們不對私有方法進行單元測試。即便如此我們還是嘗試着做了一下:

②回歸測試
  回歸測試是指修改了舊代碼后,重新進行測試以確認修改沒有引入新的錯誤或導致其他代碼產生錯誤。我們隊伍是只保留了原有項目的UI界面,故不具備進行回歸測試的條件。
  
③覆蓋率

覆蓋率解讀:
  根據生成的數據可以看到
  ⅰ.QA_List.java的覆蓋率為100%;
  ⅱ.Test.java中有一個Test方法沒有執行影響到了覆蓋率,這也是我們百思不得其解的一個問題;
  ⅲ.Work.java的覆蓋率問題是因為我們在測試時的記錄耗時太短而沒有記錄到;
  ⅳ.Frame.java的覆蓋率不高是因為我們在執行時沒有選擇進行語言的切換,在之后的測試中我們進行語言切換后,覆蓋率會大幅增加;
  ⅴ.Histroy.java中,因為在錯題文件已經存在的情況下,創建文件的代碼不會被覆蓋,從而影響了覆蓋率;
  ⅵ.Arithmetic.java由於我們保留了必要的無參構造器而降低了覆蓋率。
  
  
(6)效能分析
  效能分析我們使用老師提供的JProfiler,JProfiler的內存視圖部分可以提供動態的內存使用狀況更新視圖和顯示關於內存分配狀況信息的視圖。所有的視圖都有幾個聚集層並且能夠顯示現有存在的對象和作為垃圾回收的對象。
①整體效能分析:

②活動內存:

③線程:

④CPU:

⑤垃圾回收器:

  

4.核心代碼展示

public class Arithmetic {
	
	private static int count(char c) {
		if (c == '=')
			return 0;
		if (c == '(')
			return 1;
		if (c == '+')
			return 2;
		if (c == '-')
			return 3;
		if (c == '*')
			return 4;
		if (c == '/')
			return 5;
		if (c == '^')
			return 6;
		return 7;
	}

	
	private static boolean isNum(String exp, int pos) {
		char ch = exp.charAt(pos);
		if (ch >= '0' && ch <= '9') {
			return true;
		}
		return false;
	}

	
	private static String trans(String exp) {

		// 負數都用括號括起來
		for (int i = 0; i < exp.length(); i++) {
			if (exp.charAt(i) == '-' && exp.charAt(i - 1) == '(') {
				exp = exp.substring(0, i) + '0' + exp.substring(i, exp.length());
				i++;
			}
		}
		// System.out.println(exp);

		String postexp = new String();
		int lpri[] = { 0, 1, 3, 3, 5, 5, 7, 8 }; // =(+-*/^)
		int rpri[] = { 0, 8, 2, 2, 4, 4, 6, 1 };

		ArrayList<Character> op = new ArrayList<>();

		op.add('=');
		for (int i = 0; i < exp.length();) {
			if (isNum(exp, i)) {
				while (i < exp.length() && isNum(exp, i)) {
					postexp += exp.charAt(i);
					i++;
				}
				postexp += '#';
			} else {
				if (lpri[count(op.get(op.size() - 1))] < rpri[count(exp.charAt(i))]) {
					op.add(exp.charAt(i));
					i++;
				} else if (lpri[count(op.get(op.size() - 1))] == rpri[count(exp.charAt(i))]) {
					op.remove(op.size() - 1);
					i++;
				} else {
					postexp += op.get(op.size() - 1);
					op.remove(op.size() - 1);
				}
			}
		}

		while (!op.isEmpty() && op.get(op.size() - 1) != '=') {
			postexp += op.get(op.size() - 1);
			op.remove(op.size() - 1);
		}

		return postexp;
	}
	
	
	// 操作數不超過5個,操作符不超過4個,括號不超過3對,乘方只能有一個,而且次數只能小於等於3
	private static final int MAX_NUMS = 5;
	private static final int MIN_NUMS = 2;
	private static final int MAX_PAIRS = 2;

	
	private static String generateNewExp() {
		// 產生基本的算式

		String exp = new String();
		Random random = new Random();
		int nums = random.nextInt(MAX_NUMS - MIN_NUMS + 1) + MIN_NUMS;
		int pairs = random.nextInt(MAX_PAIRS + 1);
		HashMap<Integer, Integer> leftPosMap = new HashMap<>();
		HashMap<Integer, Integer> rightPosMap = new HashMap<>();
		for (int i = 0; i < nums; i++) {
			leftPosMap.put(i + 1, 0);
			rightPosMap.put(i + 1, 0);
		}
		for (int i = 0; i < pairs; i++) {
			int leftPos = random.nextInt(nums) + 1;
			int rightPos = random.nextInt(nums - leftPos + 1) + leftPos;
			leftPosMap.put(leftPos, leftPosMap.get(leftPos) + 1);
			rightPosMap.put(rightPos, rightPosMap.get(rightPos) + 1);
		}
		boolean hasPow = false;

		for (int i = 0; i < nums; i++) {

			boolean needContinue = false;
			
			if (i != 0) {
				int op = random.nextInt(5);
				switch (op) {
				case 0:
					exp += "+";
					break;
				case 1:
					exp += "-";
					break;
				case 2:
					exp += "*";
					break;
				case 3:
					exp += "/";
					break;
				default:
					if (hasPow) {
						needContinue = true;
						i--;
					} else {
						exp += "^";
						hasPow = true;
					}
					break;
				}
				
				if (needContinue) {
					continue;
				}

			}
			
			int leftNums = leftPosMap.get(i + 1);

			for (int j = 0; j < leftNums; j++) {
				exp += "(";
			}
			
			if (exp.length() > leftNums && exp.charAt(exp.length() - leftNums - 1) == '^') {
				int matchPos = i + 1;
				for (int j = 0; j < leftNums; j++) {
					exp = exp.substring(0, exp.length() - 1);
					while (rightPosMap.get(matchPos) <= 0) {
						matchPos++;
					}
					rightPosMap.put(matchPos, rightPosMap.get(matchPos) - 1);
				}
				exp += random.nextInt(4);
			} else {
				int generateNum = (random.nextInt(40) - 20);
				if (generateNum < 0 && (leftPosMap.get(i + 1) == 0 || rightPosMap.get(i + 1) == 0)) {
					exp += "(" + generateNum;
					rightPosMap.put(i + 1, rightPosMap.get(i + 1) + 1);
				} else {
					exp += generateNum;
				}
			}
			int rightNums = rightPosMap.get(i + 1);
			for (int j = 0; j < rightNums; j++) {
				exp += ")";
			}
		}
		return exp;
		// 加括號
	}

5.程序運行

主界面:

各語言下的答題界面:



復習:

錯題集:

答題歷史:


6.碼雲提交




7.結對編程過程展示


8.小結

一般般吧,就是一開始不是很能理解為什么禁用加法和乘法的結合律,后來才知道原來禁用了結合律才能使用標准答案就是最小表示法,之前在POJ上有做過樹的最小表示法,所以基本上就是平推過去了。(1+2)+3,第一個+是第二個+的子樹,啟用結合律的話,可以將第一個+的子樹往上提,即只維護一個+號的子樹。編程也基本上沒什么難度,只要找到加號往上找他的父節點就行了。不過沒那么多時間再改了。
兩個隊友還挺不錯的,學東西很認真,給他們復習了一遍四則運算的后綴表達式算法和Git的使用,學得都挺快的。
就是節奏稍微有點拖,我們團隊的執行力還不夠。
不過還好,報告寫得挺不錯的,嘿嘿嘿嘿嘿,反正以后團隊編程也要繼續磨合。
1+1+1>3,還闊以,還闊以,ACM配置,hhhh
(PS:還是想找一個妹子結對編程,最好是像實栗那樣的,謝謝。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM