格式描述
- 課程名稱: 軟體工程實踐
- 作業要求: 結對第二次—文獻摘要熱詞統計及進階需求
- 結對學号: 221600412 | 221600411
- Fork同名倉庫的GitHub項目位址: GitHub位址
- GitHub代碼簽入記錄:
對第二次—文獻摘要熱詞統計及進階需求 對第二次—文獻摘要熱詞統計及進階需求
作業開發過程
- 團隊分工
- 221600411工作
- 需求分析
- 代碼開發(單元測試、微信小程式)
- 代碼測試
- 部落格撰寫
- 設計函數流程圖
- 設計類圖極其關系
- 輔助開發
- 獨立完成附加小程式的編碼工作
- 221600412工作
- 需求讨論
- 代碼編寫
- 基本需求和進階需求的功能實作
- 代碼壓力測試
- 代碼優化
- 附加題的資料分析與制圖
- 221600411工作
- 效能分析和PSP
PSP是卡耐基梅隆大學(CMU)的專家們針對軟體工程師所提出的一套模型:Personal Software Process (PSP, 個人開發流程,或稱個體軟體過程)。
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
Estimate | 估計這個任務需要多少時間 | 900 | 1600 |
Development | 開發 | 350 | 500 |
Analysis | 需求分析 (包括學習新技術) | 60 | 230 |
Design Spec | 生成設計文檔 | 10 | |
Design Review | 設計複審 | ||
Coding Standard | 代碼規範 (為目前的開發制定合适的規範) | 20 | 30 |
Design | 具體設計 | ||
Coding | 具體編碼 | 270 | 450 |
Code Review | 代碼複審 | ||
Test | 測試(自我測試,修改代碼,送出修改) | 40 | |
Reporting | 報告 | 120 | |
Test Report | 測試報告 | ||
Size Measurement | 計算工作量 | ||
Postmortem & Process Improvement Plan | 事後總結, 并提出過程改進計劃 | ||
合計 | |||
解題思路
-
- 首先大概閱讀整個作業文檔,大緻了解這次作業的主要任務。
- 将整個作業根據要求分成基礎和進階,首先考慮完成基礎部分。兩人共同對作業文檔的基礎作業部分進行精細閱讀,歸納出每部分的要求,便于後續的功能設計
- 對于作業文檔中感覺有争議的要求或了解,去群裡尋找助教的熱情解答,私下與班級同學共同探讨對這個争議的了解,結合百度查閱相關的資料
- 代碼實作與測試
- 對分析後的功能要求點,對代碼開發進行初步的設計(比如包的設計,團隊兩人約定同意的代碼規範等),便于後續對代碼的管理
- 按照對代碼的設計進行代碼開發,開發過程中碰到某個知識點忘記了,就進行百度查找答案
- 每開發一個功能就進行該功能的測試,防止後續測試尋找bug的工作量太大
- 基本功能實作後,進行各類資料和各項名額的測試
- 對代碼進行複查,補上開發過程漏掉的注釋等,友善後期的維護
代碼設計過程
- 基本需求
-
項目結構
221600412&221600411
|- src
|- Main.java(主程式,可以從指令行接收參數)
|- lib.java(包含其它自定義函數,可以有多個)
- 函數關系與流程:
- 主要有兩個類 Main.java 和 Lib.java 為了友善維護和修改 函數拆成四個 主要函數countChar,countWord,countLine,countMostWord,分别為計算字元數,單詞數,行數,和輸出詞頻,通過主函數依次調用countChar,countWord,countLine,countMostWord去進行計算并輸出
-
- 進階需求:
-
|- Main.class(編譯生成的可運作程式)
|- lib.class(編譯生成的可運作程式)
|- cvpr
|- result.txt(爬蟲結果)
|- Main.java
- 爬蟲使用工具 :Java
- 爬蟲思路:首先進入CVPR官網對html文檔進行分析發現每篇論文都在的div.class="ptitle"中,是以程式先class選擇器(getElementByClass("ptitle"))擷取所有的div,接着使用元素選擇器(select("a"))擷取其中的a标簽,最後分别用(attr("href")),和(text())擷取href和文本的值,把擷取的連結和基礎的url拼接成論文的通路位址,接着進入論文的詳情頁,ID選擇器getElementById("abstract"),擷取Div的id屬性為abstract的元素并用text()擷取對應摘要的文本内容,然後依照格式輸出到指定的檔案中。如下圖
- 标題和連結部分
對第二次—文獻摘要熱詞統計及進階需求 - 摘要部分
對第二次—文獻摘要熱詞統計及進階需求 - 主要有兩個類 Main.java 和 Lib.java,主函數接收指令行參數後,設定完變量并傳遞給Lib類,Lib類通過參數控制調用哪個函數。主要函數countWordOrWordGroupAndFrequency,countWord,countWordGroup,printCountMostWord,通過函數countWordOrWordGroupAndFrequency進行控制調用那個函數和輸出
- 單元測試
- 每完成一項功能比如單詞數的統計,然後根據作業文檔盡可能設計各種可能出現的輸入情況進行測試統計,盡量確定每項功能在開發完成後是沒有bug存在
- 對總的輸出進行測試并比對輸出的資料格式
- 最後對要上傳的代碼,先删掉代碼中的中文字元和一些無用代碼注解 并用javac 和 java 進行編譯運作確定程式在送出後能正确編譯運作
- 單元測試圖
對第二次—文獻摘要熱詞統計及進階需求 對第二次—文獻摘要熱詞統計及進階需求
- 代碼組織與内部實作設計
- 基本需求類圖
對第二次—文獻摘要熱詞統計及進階需求 - 進階需求類圖
- 基本需求類圖
- 算法的關鍵與關鍵實作部分流程圖
- 基本需求算法的關鍵主要是countChar,countWord,countLine,countMostWord
- 進階需求算法的關鍵主要是countWordOrWordGroupAndFrequency,countWord,countWordGroup,printCountMostWord
- 具體算法思路在 下面的代碼說明
- 基本需求流程圖如下
對第二次—文獻摘要熱詞統計及進階需求 - 進階需求的流程圖如下
對第二次—文獻摘要熱詞統計及進階需求
改程序式性能
- 花費的時間:120分鐘
- 改進思路 :
- 對檔案的讀取一開始分成計算char,word,line分成三個函數分别然後三個函數互相獨立,計算的時候分别讀取三次的檔案IO,導緻程式跑得慢,之後改進檔案的讀取次數,主函數從檔案讀取資料之後把資料存在記憶體中,然後在對記憶體中的資料進行操作,這樣隻進行了一遍IO操作,節省了兩次IO。
- 把字元串切割成單詞,首先是最簡單的想法,直接double for 進行周遊把分割符位置全部标出來,并進行切割。改進後:用單重for循環進行周遊用辨別符辨別單詞的首尾,接着進行切割單詞并判斷是否為題目所要求的單詞。改進後的結構為O(n)
- 對于進階需求,雖然把功能也才成三個函數,但在處理資料的時候,隻進行一遍的處理,也就是程式隻進行一次IO和一次for循環,時間複雜度達到O(n)。思路就是建立幾個儲存資料的字段,我的處理論文的方式,先把資料讀入到記憶體中,之後對論文進行切分,切分後一篇論文包含,編号,标題,和摘要,之後對這些資料分開處理,同時計算字元,單詞,行數,和詞組,對每篇論文處理的結果和字段中的結果累加,達到了隻對資料處理一遍就能完成計算。
- 性能分析圖:
對第二次—文獻摘要熱詞統計及進階需求 對第二次—文獻摘要熱詞統計及進階需求 - 消耗最大的函數countWord()和 countGroupWord()
- 複雜度為均為 O(n) 都對資料進行一次周遊 得到計算結果
代碼說明
-
- 思路與說明:首先程式從指令行讀取檔案明,然後通過檔案名對檔案的内容進行讀取到字元串中,然後分别對這個字元串進行countChar,countWord,countLine,countMostWord,分别為計算字元數,單詞數,行數,和輸出詞頻,通過主函數依次調用countChar,countWord,countLine,countMostWord去進行計算并輸出
- 主函數流程,依此調用函數并輸出到指定檔案中
public static void main(String args[]) throws IOException {
String content = readFile(args[0]);
Lib lib = new Lib();
String str1 = lib.countChar(content);
String str2 = lib.countWord(content);
String str3 = lib.countLine(content);
String str4 = lib.countMostWord();
writeFile("result.txt", str1 + "\r\n" + str2 + "\r\n" + str3 + "\r\n" + str4);
}
- countChar,對計算字元 一個特例 \r\n 這個原本ascii碼為13,10算做兩個字元,作業要求隻算一個字元是以關鍵代碼如下
public String countChar(String content) {
return "characters: " + content.replaceAll("\r\n", " ").toCharArray().length;
}
- countWord,對計算單詞,通過單層for循環進行周遊,先找到兩個分隔符,然後用函數substring切出單詞并用toLowerCase全部轉成小寫,然後對這個單詞判斷是否為 題目需求定義的單詞,是的話存放到Map中,不是的話繼續循環。
public String countWord(String content) {
int wordNum = 0;
char[] ch = content.toCharArray();
int begin = 0, end = 0, len = content.toCharArray().length;
String str = null;
for (int i = 0; i < len; i++) {
boolean flag = !((ch[i] >= 65 && ch[i] <= 90) || (ch[i] >= 97 && ch[i] <= 122) || (ch[i] >= 48 && ch[i] <= 57));
if (flag || i == 0) { // If it is a delimiter or the beginning of the calculation
if (flag) {
begin = end = i + 1;
} else {
begin = end = i;
}
// Find two delimiters
while (end < len && ((ch[end] >= 65 && ch[end] <= 90) ||
(ch[end] >= 97 && ch[end] <= 122) || (ch[end] >= 48 && ch[end] <= 57))) {
end++;
}
if (begin != end) {
i = end - 1;
if (end - begin >= 4) {
str = content.substring(begin, end).toLowerCase();
boolean isWord = true;
for (int j = 0; j < 4; j++) { // If the first four are letters
if (str.charAt(j) >= 48 && str.charAt(j) <= 57) {
isWord = false;
break;
}
}
if (isWord) {
wordNum++;
if (map.containsKey(str)) {
map.put(str, map.get(str) + 1);
} else {
map.put(str, 1);
}
}
}
}
} else {
continue;
}
}
return "words: " + wordNum;
}
- 對于計算行數,通過循環先判斷有沒有非空白字元,有非空白字元的話進行标記,直到找到回車換行符然後,結束
public String countLine(String content) {
int len = content.toCharArray().length;
char[] ch = content.toCharArray();
int line = 0;
boolean flag = false;
for (int i = 0; i < len; i++) {
while (i + 1 < len) { // /r/n Text wrap skips calculations
if ((ch[i] == 13 && ch[i + 1] == 10)) {
break;
}
if ((ch[i] >= 0 && ch[i] <= 32) || ch[i] == 127) { // Is a blank character
i++;
continue;
} else {
i++;
flag = true;
}
}
if( i + 1 == len && ! ((ch[i] >= 0 && ch[i] <= 32) || ch[i] == 127)){
flag = true;
}
i = i + 1;
if (flag) {
line++;
flag = false;
}
}
return "lines: " + line;
}
- 輸出詞頻,上面把所有的單詞存放到map中,通過這個函數對mao進行排序之後輸出
// Computing word frequency
public String countMostWord() {
// Sort the keys in the HashMap and display the sorted results
List<Map.Entry<String, Integer>> infoIds = new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
// Sort the keys in the HashMap
Collections.sort(infoIds, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1,
Map.Entry<String, Integer> o2) {
if (o1.getValue() == o2.getValue()) {
return o1.getKey().compareTo(o2.getKey());
}
return -(o1.getValue() - o2.getValue());
}
});
StringBuilder sb = new StringBuilder();
// The output frequency
for (int i = 0; i < infoIds.size() && i < 10; i++) {
sb.append("<" + infoIds.get(i).getKey() + ">: " + infoIds.get(i).getValue() + "\r\n");
}
return sb.toString();
}
- 進階需求
- 思路與說明
- 爬蟲思路在上面已提到
- 主函數通過接收指令行參數,并調用相對應的函數,對一些字段進行設定值,然後通過調用Lib類中的countWordOrWordGroupAndFrequency函數進行計算,最後輸出到指定的檔案中
- 思路與說明
public String inputFileName = null; // The file name to read
public String outputFileName = null; // The file name to output
public int titleWeight = 10; // title weight
public int abstractWeight = 1; // abstract weight
public int m = 1; // The length of the phrase
public int n = 10; // Output the number of words
public static void main(String args[]) throws IOException {
Main instance = new Main();
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "-i":
instance.I(args[i + 1]);
break;
case "-o":
instance.O(args[i + 1]);
break;
case "-w":
instance.W(args[i + 1]);
break;
case "-m":
instance.M(args[i + 1]);
break;
case "-n":
instance.N(args[i + 1]);
break;
default:
break;
}
}
String content = instance.readFile(instance.inputFileName);
Lib lib = new Lib();
lib.countWordOrWordGroupAndFrequency(content, instance.titleWeight, instance.abstractWeight, instance.m, instance.n); // 計算單詞總數
String str1 = lib.printChar(); // calculate Number of characters
String str2 = lib.printWord(); // Count words
String str3 = lib.printLines(); // Calculate number of lines
String str4 = lib.printCountMostWord(instance.n);// Computing word frequency
instance.writeFile(instance.outputFileName,str1 + "\r\n" + str2 + "\r\n" + str3 + "\r\n" + str4);
}
public void I(String str) {
inputFileName = str;
}
public void O(String str) {
outputFileName = str;
}
public void W(String str) {
if (Integer.valueOf(str) == 0) {
titleWeight = abstractWeight = 1;
} else if (Integer.valueOf(str) == 1) {
titleWeight = 10;
abstractWeight = 1;
}
}
public void M(String str) {
m = Integer.valueOf(str);
}
public void N(String str) {
n = Integer.valueOf(str);
}
- countWordOrWordGroupAndFrequency接收一系列的參數( content, int w1, int w2, int m1, int n1),然後分别對content進行論文切分,通過論文之間的兩個回車換行符把論文分開,對每篇論文也用回車換行進行切分,分成編号,标題和摘要。之後對标題和摘要分别進行統計,統計後存放在字段中,之後調用printChar,printWord,printLines,printCountMostWord輸出到指定檔案中
public void countWordOrWordGroupAndFrequency(String content, int w1, int w2, int m1, int n1) {
titleWeight = w1;
abstractWeight = w2;
m = m1;
n = n1;
boolean isWordGroup = false;
if (m >= 2) {
isWordGroup = true;
}
String[] attr = content.split("\r\n\r\n");
String[] str = null;
String t1 = null;
String t2 = null;
for (int i = 0; i < attr.length; i++) {
if (attr[i] != null && !attr[i].equals("")) {
if (i == 0) {
str = attr[i].split("\r\n");
} else {
str = attr[i].replaceFirst("\r\n", "").split("\r\n");
}
if (str.length == 3) {
t1 = str[1].substring(7);
t2 = str[2].substring(10);
lines += 2; // add line
charNum += 2; // add line
countChar(t1 + t2);
if (isWordGroup) {
countWordGroup(t1, true); // Calculate the title
countWordGroup(t2, false); // Calculated in this paper,
} else {
countWord(t1, true); // Calculate the title
countWord(t2, false); // Calculated in this paper
}
}
}
}
}
- countWord,countWordGroup 兩個算法思路很相似,都是先進行切分單詞,對于countWordGroup切分後的第一個單詞标記起始位置如果連續m個都為單詞,切出起始位置和末尾的位置,然後存到map中,存放的時候因為上面已經把title和摘要進行了分開計算是以在存放的時候,能知道當處理的是摘要還是标題,然後用正确的權重去權重,最後調用排序算法後輸出。
public void countWordOrWordGroupAndFrequency(String content, int w1, int w2, int m1, int n1) {
titleWeight = w1;
abstractWeight = w2;
m = m1;
n = n1;
boolean isWordGroup = false;
if (m >= 2) {
isWordGroup = true;
}
String[] attr = content.split("\r\n\r\n");
String[] str = null;
String t1 = null;
String t2 = null;
for (int i = 0; i < attr.length; i++) {
if (attr[i] != null && !attr[i].equals("")) {
if (i == 0) {
str = attr[i].split("\r\n");
} else {
str = attr[i].replaceFirst("\r\n", "").split("\r\n");
}
if (str.length == 3) {
t1 = str[1].substring(7);
t2 = str[2].substring(10);
lines += 2; // add line
charNum += 2; // add line
countChar(t1 + t2);
if (isWordGroup) {
countWordGroup(t1, true); // Calculate the title
countWordGroup(t2, false); // Calculated in this paper,
} else {
countWord(t1, true); // Calculate the title
countWord(t2, false); // Calculated in this paper
}
}
}
}
}
// Count words
public void countWord(String content, boolean isTitle) {
char[] ch = content.toCharArray();
int begin = 0, end = 0, len = content.toCharArray().length;
String str = null;
for (int i = 0; i < len; i++) {
boolean flag = !((ch[i] >= 65 && ch[i] <= 90) || (ch[i] >= 97 && ch[i] <= 122) || (ch[i] >= 48 && ch[i] <= 57)); // 判斷是否是分隔符
if (flag || i == 0) { // If it is a delimiter or the beginning of the calculation
if (flag) {
begin = end = i + 1;
} else {
begin = end = i;
}
// Find two delimiters
while (end < len && ((ch[end] >= 65 && ch[end] <= 90) ||
(ch[end] >= 97 && ch[end] <= 122) || (ch[end] >= 48 && ch[end] <= 57))) {
end++;
}
if (begin != end) {
i = end - 1;
if (end - begin >= 4) {
str = content.substring(begin, end).toLowerCase();
boolean isWord = true;
for (int j = 0; j < 4; j++) { // If the first four are letters
if (str.charAt(j) >= 48 && str.charAt(j) <= 57) {
isWord = false;
break;
}
}
if (isWord) {
wordNum++;
if (map.containsKey(str)) {
if (isTitle) {
map.put(str, map.get(str) + titleWeight);
} else {
map.put(str, map.get(str) + abstractWeight);
}
} else {
if (isTitle) {
map.put(str,titleWeight);
} else {
map.put(str,abstractWeight);
}
}
}
}
}
} else {
continue;
}
}
}
// Computing phrase
public void countWordGroup(String content, boolean isTitle) {
char[] ch = content.toCharArray();
int wordGroupBegin = 0, wordGroupEnd = 0;
int firstWordEnd = 0;
int begin = 0, end = 0, len = content.toCharArray().length;
String str = null;
int wordGroupNum = 0;
for (int i = 0; i < len; i++) {
boolean flag = !((ch[i] >= 65 && ch[i] <= 90) || (ch[i] >= 97 && ch[i] <= 122) || (ch[i] >= 48 && ch[i] <= 57)); // Determines if it is a delimiter
if (flag || i == 0) { // If it is a delimiter or the beginning of the calculation
if (flag) {
begin = end = i + 1;
} else {
begin = end = i;
}
// Find two delimiters
while (end < len && ((ch[end] >= 65 && ch[end] <= 90) ||
(ch[end] >= 97 && ch[end] <= 122) || (ch[end] >= 48 && ch[end] <= 57))) {
end++;
}
if (begin != end) {
i = end - 1;
if (end - begin < 4) {
wordGroupNum = 0;
}
if (end - begin >= 4) {
str = content.substring(begin, end).toLowerCase();
boolean isWord = true;
for (int j = 0; j < 4; j++) { // If the first four are letters
if (str.charAt(j) >= 48 && str.charAt(j) <= 57) {
isWord = false;
wordGroupNum = 0;
break;
}
}
if (isWord) {
wordNum++;
wordGroupNum++;
if (wordGroupNum == 1) {
wordGroupBegin = begin;
firstWordEnd = end;
}
if (wordGroupNum == m) {
wordGroupEnd = end;
str = content.substring(wordGroupBegin, wordGroupEnd).toLowerCase();
if (map.containsKey(str)) {
if (isTitle) {
map.put(str, map.get(str) + titleWeight);
} else {
map.put(str, map.get(str) + abstractWeight);
}
} else {
if (isTitle) {
map.put(str,titleWeight);
} else {
map.put(str,abstractWeight);
}
}
wordGroupNum = 0;
i = firstWordEnd - 1;
wordNum = wordNum - m +1 ;
}
} else {
wordGroupNum = 0;
}
}
}
} else {
continue;
}
}
}
單元測試代碼
- 測試代碼
@org.junit.Test
public void Test10() throws IOException {
String arg = "H:\\javaweb代碼\\qqqqq\\src\\main\\java\\軟工實踐_test\\input.txt";
String content = readFile(arg);
Lib lib = new Lib();
String str1 = lib.countChar(content); // 計算 字元數
String str2 = lib.countWord(content); // 計算單詞總數
String str3 = lib.countLine(content); // 計算行數
String str4 = lib.countMostWord();// 計算詞頻
String test1="characters: 99";
String test2="words: 20";
String test3="lines: 20";
String test4="<aaaa>: 4\n" +
"<bbbb>: 1\n" +
"<cccc>: 1\n" +
"<dddd>: 1\n" +
"<eeee>: 1\n" +
"<ffff>: 1\n" +
"<gggg>: 1\n" +
"<hhhh>: 1\n" +
"<iiii>: 1\n" +
"<jjjj>: 1\n";
Assert.assertEquals(str1,test1);
Assert.assertEquals(str2,test2);
Assert.assertEquals(str3,test3);
Assert.assertEquals(str4,test4);
}
- 部分測試資料
qq q qqq qqq.qqq
q www w www
結果
characters: 35
words: 0
lines: 2
a
b
c
d
結果
characters: 11
words: 0
lines: 4
abcdefghijklmnopqrstuvwxyz
1234567890
,./;'[]\<>?:"{}|`-=~!@#$%^&*()_+
結果
characters: 76
words: 1
lines: 3
<abcdefghijklmnopqrstuvwxyz>: 1
- 測試的思路與想法
- 測試的思路與想法主要在于對各種不滿足條件的情形進行測試,特别是在臨界條件,測試程式能否正确統計各種特殊情況
- 開頭帶有空格、tab的情形
- 整個文檔隻有空白字元的情形
- 對于單詞的檢查,根據要求設計各種不滿足的情況,觀察是否可以正确統計
- 針對空白字元,設計多種情況進行測試
- 對回車換行做獨立的測試
- 對\r\n的特殊輸入進行測試
- 對各類邊界條件進行多次反複的測試
- 測試的思路與想法主要在于對各種不滿足條件的情形進行測試,特别是在臨界條件,測試程式能否正确統計各種特殊情況
困難及解決和評價隊友
- 評價221600411
- 值得學習的地方:對作業一絲不苟,對程式的測試非常嚴謹,流程圖和類圖制作得很好
- 需要改進的地方:需要加強時間的管理,平衡好作業和其他的時間。
- 評價221600412
- 值得學習的地方:動手實踐能力較強,獨自完成了大部分的編碼工作,盡自己最大的能力完成作業。
- 需要改進的地方:需平衡好時間的,加強理論的學習
- 所遇困難
- 需求了解花了許多時間去了解,有些需求了解錯誤導緻程式多次變動
- 一些程式軟體沒有安裝,比如性能測試工具
- 解決辦法
- 查找助教的解答,與同學探讨
- 通過百度查閱相關資料進行安裝和操作
附加題設計與展示
- 資料分析使用python 庫
- 資料分析
- 分别對标題和摘要進行了詞頻統計并輸出成 詞雲
- 标題詞雲
對第二次—文獻摘要熱詞統計及進階需求 - 摘要詞雲
對第二次—文獻摘要熱詞統計及進階需求 - 下圖為對作者發表論文的svg圖顯示前30位 連結 點選連結可以在網站上檢視svg圖下圖隻有靜态效果
- 設計的創意
- 針對進階要求所爬取的資料繁多,在控制台的檢視較為費勁,便開發論文清單的界面,便于使用者對爬取的論文進行浏覽;考慮到提高使用者的簡便程度,決定使用微信小程式進行簡單的界面顯示
- 實作思路
- 後端使用java開發接口,将爬取的資料存取在資料庫,通過接口與微信小程式對接,傳遞資料,實作論文清單的界面顯示與浏覽
- 實作成果的展示
對第二次—文獻摘要熱詞統計及進階需求 對第二次—文獻摘要熱詞統計及進階需求 對第二次—文獻摘要熱詞統計及進階需求 - 詞頻分析 代碼下載下傳
- 心得體會
陳迎仁
- 心得與總結
通過這次作業學會了許多除了編寫代碼外的知識,比如GitHub的上傳,單元測試的含義,以及測試資料的設計,等各種非代碼類的技能。當然在代碼這方面也提升不少,主要在于測試階段,設計各類流程圖以及類圖還有微信小程式的界面開發以及部落格文檔的撰寫,輔助隊友進行代碼開發。通過這次團隊作業,我進一步對團隊協作有了深的了解,一個人完成代碼開發以及測試真的工作量大,而且在對于測試,客觀性不夠強,團隊配合測試,所使用的測試用例可能更加的全面以及更能夠保證測試結果的正确性。總之在這次軟工實踐作業中,雖然花費的時間較多,但是也學到了許多新的技能。累但值得哈哈哈!
陳宇
-
這次作業的作業量雖然很大,但是通過這次作業我學到了很多東西,比如一個明确的需求有多重要,這個作業的需求研究了好幾天,最後還是通過微信群裡的同學進行了讨論,還有助教的回答。之後就是編碼和測試,在編碼的過程中,一開始對需求的不明确導緻一直修改代碼,後面等确定了需求後在寫之後進度快了好多,編寫完代碼之後就和隊友一起測試代碼,調試代碼,知道程式的運作的結果和周圍隊友的結果一緻,雖然這次作業花了很多的時間,也學到了很多的東西,也學到了很多和團隊配合的東西。