這個作業屬于哪個課程 | 2021春軟體工程實踐|W班(福州大學) |
---|---|
這個作業要求在哪裡 | 寒假作業2/2 |
作業正文 | 再次閱讀《建構之法》并提出問題、編寫詞頻統計器的詳細過程 |
其他參考文獻 | ... |
目錄:
- 再次閱讀《建構之法》,提出疑問
- 軟體工程發展過程的冷知識和趣事
- WordCount開發過程
1.再次閱讀《建構之法》,提出疑問
問題一:大學中的團隊是否不太适合MSF模式?
MSF模式中,充分的授權和信任是原則之一。但是,充分的授權和信任在大學的團隊程式設計中有些時候并不會有好的效果,而是很多人都不自覺,寫的代碼也隻是應付了事。書上提出了一個問題:是否團隊中需要什麼獎勵機制來促進,或者完全靠成員的自覺?書上的讨論中提出了一個觀點:如果項目沒有商業價值,照搬商業軟體的做法不合适。在之前的學期中有一個實踐作業中,我們團隊也遇到類似的問題,在實踐中我們每個人有了特定分工,但是效率并沒有得到提升,最後也是草率完成項目。我的疑問是大學中的團隊是否不太适合MSF模式?
問題二:團隊模式與團隊開發模式中是否可以找到最合适的組合使得團隊開發效率最高?
書中提到了團隊模式主要有:主治醫師模式、明星模式、社群模式、業餘劇團模式、秘密團隊模式、特工團隊模式、交響樂團模式、爵士樂模式、功能團隊模式、官僚模式;團隊的開發模式主要有:寫了再改模式、瀑布模式及其變形、老闆驅動流程、漸進傳遞流程、靈活流程;團隊模式是團隊組織的定性,團隊的開發模式是團隊進行軟體開發使用的方法。我的疑問是團隊模式與團隊開發模式中是否可以找到最合适的組合使得團隊開發效率最高?
問題三:創新在考慮到好的創意的同時還應該注意哪些因素,才更容易被大衆所接受?
創新需要好的想法,但是好的想法并不足以讓一個創新為所有人接受。以鍵盤為例,我們現在普遍使用QWERTY 鍵盤, 那麼隻有10% 的英語單詞能在手指不離開 home Row (ASDF 的那一排) 的情況下敲出來。 但是如果使用 Dvorak 鍵盤布局, 你可以在home row
打出 60% 的單詞(所有的元音都在 home row)!這樣會減輕手指和相關肌肉的負擔, 減少勞損, 同時加快打字速度。但是Dvorak鍵盤在如今并沒有流行起來。我的疑問是:創新在考慮到好的創意的同時還應該注意哪些因素,才更容易被大衆所接受?
問題四:為什麼有些時候領域内的專家并沒有領域外的創新者那麼有創意?
在我們的認知中,創新大部分是該領域的專家才能做到的。但是,事實并非如此,很多創新者表示他們最成功的創新并不是自己最擅長的領域。例如,現在中國甚至全世界,B2B網站做的最好的阿裡巴巴的創始人,并不是計算機相關領域的行家;Nokia公司在進入通信
領域之前隻是一家做森林木材産品相關的企業;還有很多類似的例子,我的疑問是為什麼有些時候領域内的專家并沒有領域外的創新者那麼有創意?是因為“不識廬山真面目,隻緣身在此山中”嗎?
問題五:技術創新是否是創新的關鍵?如果技術創新不是最關鍵的,那麼什麼因素是決定創新成敗最關鍵的因素?
書中有提到一種名為“銥星計劃”的手機,它凝聚了多種先進的技術,隻要有66顆衛星覆寫地球表面,人們就可以随時随地打電話,這聽起來是比現在的基站好太多了,但是這項計劃不到一年就申請破産保護。這計劃利用衛星電話,技術上确實有很大創新,但是最後卻失敗了。我的疑問是:技術創新是否是創新的關鍵?如果技術創新不是最關鍵的,那麼什麼因素是決定創新成敗最關鍵的因素?
問題六:是否有更好的績效評估方法,能正确評估團隊中每個人真正的價值?
一個團隊能不斷迸發創作的熱情,需要有績效評估。績效評估又有各種不同方面的評估,書中有提到按照角色來定位,但是這種方法又有些不足,不是所有人都會服從别人給自己的角色定位,他們往往會将自己放在對自己有利的角色定位上;也有一種說法是比資曆,但是軟體行業有赢者通吃的規律,并不是資曆越老越有話語權;還有比效率、背靠背評比等方法。但是這些方法都不是最好的。還有二維績效評估,這方法看似合理。我的疑問是:是否有更好的績效評估方法,能正确評估團隊中每個人真正的價值?
2.軟體工程發展過程的冷知識和趣事
我們都知道,程式員在程式設計時必須定義程式用到的變量,以及這些變量所需的計算機記憶體,這些記憶體用比特位定義。一個16位的變量可以代表-32.768到32.767中間的值。而一個64位的變量可以代表−9.223.372.036.854.775.808到9.223.372.036.854.775.807中間的值。
1996年6月4日,阿麗亞娜5型運載火箭的首次發射點火後,火箭開始偏離路線,最終被逼引爆自毀,整個過程隻有短短30秒。阿麗亞娜5型運載火箭基于前一代4型火箭開發。在4型火箭系統中,對一個水準速率的測量值使用了16位的變量及記憶體,因為在4型火箭系統中反複驗證過,這一值不會超過16位的變量,而5型火箭的開發人員簡單複制了這部分程式,而沒有對新火箭進行數值的驗證,結果發生了緻命的數值溢出。發射後這個64位帶小數點的變量被轉換成16位不帶小數點的變量,引發了一系列的錯誤,進而影響了火箭上所有的計算機和硬體,癱瘓了整個系統,因而不得不選擇自毀,4億美金變成一個巨大的煙花。
這一則故事告訴我們在計算機中,數值溢出的錯誤是非常嚴重的,在程式設計的時候要盡量給變量設定相對大的變量類型。
3.WordCount開發過程
1.Github項目位址
Github項目位址
2.PSP表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
• Estimate | • 估計這個任務需要多少時間 | 1200 | 1080 |
Development | 開發 | ||
• Analysis | • 需求分析 (包括學習新技術) | 180 | 120 |
• Design Spec | • 生成設計文檔 | 60 | |
• Design Review | • 設計複審 | 30 | |
• Coding Standard | • 代碼規範 (為目前的開發制定合适的規範) | ||
• Design | • 具體設計 | 90 | |
• Coding | • 具體編碼 | 300 | 320 |
• Code Review | • 代碼複審 | 140 | |
• Test | • 測試(自我測試,修改代碼,送出修改) | ||
Reporting | 報告 | ||
• Test Repor | • 測試報告 | ||
• Size Measurement | • 計算工作量 | ||
• Postmortem & Process Improvement Plan | • 事後總結, 并提出過程改進計劃 | ||
合計 |
3.解題思路
看到題目的要求,我就想将這兩個打開檔案輸入檔案和打開輸出檔案編寫為兩個函數。然後統計字元數、統計單詞數、統計空行數、輸出頻數前十的單詞數分别作為一個函數,供主函數調用,這樣劃分每個功能塊比較獨立。
劃分上述功能塊之後,主函數就比較簡單,隻要調用統計字元、統計單詞數、統計行數、輸出頻數前十的函數。
4.代碼規範
我的代碼規範
5.各個子產品的設計與實作
此次作業的代碼中含有兩個類:WordCount 和Lib
Lib類中含有以下函數:
public class Lib {
public static BufferedReader openInputFile(String fileName) {
//打開輸入檔案
}
public static BufferedWriter openOutputFile(String fileName) {
//打開輸出檔案
}
public static void characterCount(String inputFile,BufferedWriter bufferedWriter){
//計算字元數
}
public static void lineCount(String inputFile,BufferedWriter bufferedWriter){
//計算行數
}
public static String [] wordCount(String inputFile,BufferedWriter bufferedWriter){
//計算單詞數
}
public static void printWord(BufferedWriter bufferedWriter,String [] resultStr){
//輸出詞頻前十的單詞數
}
}
WordCount類中含有mian函數:
public class WordCount {
public static void main(String args[]){
String [] resultStr = null;
BufferedWriter bufferedWriter = Lib.openOutputFile(args[1]);
//調用各個子產品對應的函數
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
檔案讀取和輸出,我在查找資料以後決定用BufferedReader和BufferedWriter。這樣做在檔案較大的時候可以提高讀取和存儲效率。這兩個函數較為簡單,核心代碼如下:
打開輸入檔案
BufferedReader bufferedReader = null;
bufferedReader = new BufferedReader(new FileReader(fileName));
打開輸出檔案
BufferedWriter bufferedWriter = null;
bufferedWriter = new BufferedWriter(new FileWriter(fileName));
統計字元數的的函數,我上網查詢了如何從檔案讀取字元,在閱讀了一些部落格之後,用BufferedReader的read函數逐個讀取字元,用count計數累加。核心代碼如下:
while((temp = bufferedReader.read()) != -1){
count ++;
}
bufferedWriter.write("characters:"+count+'\n');
統計單詞數的函數,我想到了正規表達式,可以比對一定格式的字元串,那麼問題就在讀取檔案中的字元并按照非字母數字為分隔符将檔案中的字元串拆分開來,再拼接成新的字元串,再将所有字母轉為小寫,最後用正規表達式進行比對,用totalcount來統計單詞數。這個函數還傳回了一個已經處理過的字元串數組,供輸出單詞頻數使用,核心代碼如下:
while((originStr = bufferedReader.readLine())!=null){
tempBuffer.append(originStr+" ");
}
bufferedReader.close();
tempStr = tempBuffer.toString().toLowerCase();
resultStr = tempStr.split("[^a-zA-Z0-9]+");
for (String s : resultStr) {
if (s.matches("[a-z]{4}[a-z0-9]*")) {
totalCount++;
}
}
bufferedWriter.write("Words:"+totalCount+'\n');
統計行數的函數,我用BufferedReader中的readline函數讀取一行,再用正規表達式進行篩選,如果是空行,則統計行數的count不加一。核心代碼如下:
while ((temp = bufferedReader.readLine()) != null) {
//如果讀出來的行不是空行(包含隻含有空格的行),行數加1
if(!temp.matches("[\\s]*")) count++;
}
bufferedReader.close();
bufferedWriter.write("lines:"+count+'\n');
輸出頻數前十的函數,首先我想到統計單詞數的函數已經周遊過檔案,并且找出來所有單詞,那麼可以讓統計單詞數的函數傳回一個已經處理過的字元串,傳給這個函數,然後統計該字元串中的單詞,存入Map中。再将Map中的單詞進行降序排列,如果頻數一樣按照字典序排列。核心代碼如下:
for (String s : resultStr) {
if (s.matches("[a-z]{4}[a-z0-9]*")) {
if (resultMap.containsKey(s)) {
//containsKey()方法用于檢查特定鍵是否在TreeMap中映射
count = resultMap.get(s);
resultMap.put(s, count + 1);
} else {
resultMap.put(s, 1);
}
}
}
//通過比較器實作排序
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(resultMap.entrySet());
//按降序排序
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> first, Map.Entry<String, Integer> second) {
if(second.getValue().compareTo(first.getValue()) == 0) {
//如果單詞頻數相同,傳回字典序較大的單詞
return second.getKey().compareTo(first.getKey());
}
//傳回兩個單詞出現次數較多的那個單詞的出現次數
return second.getValue().compareTo(first.getValue());
}
});
6.性能改進
1.在檔案讀取方面,由于提前查詢 資料,了解到BufferedReader和BufferdWriter的讀存效率更高,是以在讀寫方面在編寫程式前已經做到了性能改進。
2.統計字元數和輸出詞頻高的單詞兩個函數有一部分重複工作,是以在統計完單詞數以後傳回從檔案讀出并且劃分成一個個單詞的字元串數組。供輸出單詞頻數前十的函數使用。
7.各種測試
7.1 單元測試
編寫了一個LibTest類來測試,以下為部分測試代碼:
@Test
public void characterCountTest(){
BufferedWriter bufferedWriter = Lib.openOutputFile("src/output1.txt");
int count = 7;
Assert.assertEquals(count,Lib.characterCount("src/input1.txt",bufferedWriter));
}
@Test
public void lineCountTest(){
BufferedWriter bufferedWriter = Lib.openOutputFile("src/output1.txt");
int count = 0;
Assert.assertEquals(count,Lib.lineCount("src/input1.txt",bufferedWriter));
}
characterCountTest()是關于統計字元的函數的測試,測試思路是先統計input1.txt檔案總字元數,将count設定為該值,然後判斷count是否與characterCount函數傳回值相等,相等則正确。
lineCountTest()是關于統計行數的函數的測試,測試思路是先統計input1.txt檔案的總有效行數,将count設定為該值,然後判斷count是否與lineCount函數的傳回值相等,相等則正确。
測試覆寫率截圖:

優化覆寫率的方法:盡量設定合理的測試規則,多一些測試資料。
7.2 幾個測試樣例
(1)隻包含6個Enter鍵(Windows下回車視為"\r\n"兩個字元)
(2)先輸出6個'\n'到檔案中,再用程式處理檔案
(3)測試例子中包含多個重複單詞,這裡的換行為"\r\n"
結果如下
(4)一行中有多個單詞,用一些非字母數字元号隔開
(5)大量的字元資料
通過代碼産生資料
程式運作時間
以上為部分測試樣例。在測試過程中,我發現我的程式處理一些量少的、較為特殊的樣例的時候,效率還行,但是如果檔案較大,相應的運作時間就偏長。
8.異常處理
本次作業中,沒有編寫獨立的異常處理類,使用java中已有的IOException。在打開檔案以及關閉檔案出錯的時候,程式會報錯。
8.心路曆程以及收獲
做這個作業之前,我沒有正确預估該作業需要花的時間,也沒有先把作業中要求的程式劃分子產品,再一點點完成,最後完成得有點匆忙。我從中得到教訓,做一個項目要将大項目拆分為多個小子產品,再一步步完成每個項目,這樣可以化難為易,也可以減輕作業負擔。
在本次項目中,我複習了java語言的基礎,對于java得檔案讀取和存儲有了更深的了解。我還學習到了如何用github來管理項目。github在管理項目方面友善快捷,可以和他人共享開發的項目,也友善和他人合作程式設計。