系統分析與設計課程專案 WordCount 結對程式設計
系統分析與設計課程專案 WordCount 結對程式設計
作業說明
合作者:
201631084230(只有我一個人,“合作者”這個標題有些不合適了)
程式碼地址:
https://gitee.com/mxhkkk/Wc/tree/complete/
本次作業的連結地址:
https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188
為什麼只有我一個人
個人專案完成後,我看了很多人的作業部落格,也評論了很多,但始終沒有發出這樣一條評論:“我們的程式設計風格很像,結對程式設計我們一起吧”,確實是沒有發這樣的評論。後來才發現,團隊專案組隊因此簡單了,一個人再找一個兩人組就可以了。
PSP表格
PSP2.1 | PSP階段 | 預估耗時(分鐘) | 實際耗時(分鐘) |
Planning | 計劃 | 30 | 20 |
Estimate | 估計這個任務需要多少時間 | 30 | 20 |
Development | 開發 | 290 | 640 |
Analysis | 需求分析 (包括學習新技術) | 30 | 20 |
Design Spec | 生成設計文件 | 0 | 0 |
Design Review | 設計複審 (和同事稽核設計文件) | 0 | 0 |
Coding Standard | 程式碼規範 (為目前的開發制定合適的規範) | 20 | 20 |
Design | 具體設計 | 60 | 240 |
Coding | 具體編碼 | 60 | 120 |
Code Review | 程式碼複審 | 30 | 90 |
Test | 測試(自我測試,修改程式碼,提交修改) | 90 | 30 |
Reporting | 報告 | 90 | 120 |
Test Report | 測試報告 | 0 | 0 |
Size Measurement | 計算工作量 | 0 | 0 |
Postmortem & Process Improvement Plan | 事後總結, 並提出過程改進計劃 | 30 | 30 |
合計 | 470 | 730 |
自審
- 命令列引數解析,不準確,沒有錯誤提示資訊。我現在意識到這是一個複雜的過程,決定使用CLI來重構。
- 根據檔名來生成檔案的操作類,這也挺複雜的。有三種不同形式的檔案,檔名必須是合法的,檔名中可能帶有路徑,還有檔案萬用字元的處理,是否支援資料夾操作以及資料夾的遞迴。
- 統計過程是低效的,在設計和效能之間無法做到很好地權衡。
- 結果類似乎沒有必要應用組合模式。
- 不能很好地利用流的思想來做設計。
修復問題及關鍵程式碼
刪除Command層
發現現在的Command類完全變成了一個委託類,刪除這一層次。
Parser返回handler的集合,Invoker直接呼叫handler執行方法。
新增並行策略
刪除GUI的呼叫策略,改為並行和序列兩種呼叫策略。當處理檔案有多個且計算機支援並行時採用並行策略,否則採用序列策略。
下面驗證一下並行的效果如何:
並行的效果還是很明顯的,我測試用的計算機是雙核的,如果是四核、八核的計算機那就更快了,因為我在設計中考慮到了伸縮性。
測試檔案的地址:https://gitee.com/mxhkkk/Wc/blob/complete/src/test/java/test.zip
有趣的是,測試資料夾大小有60多MB,而壓縮後只有不到1MB,這是因為資料夾中有很多重複的檔案,為了方便我直接複製進去的。這個現象引發了我對Wc程式的一個思考,我們也可以像壓縮軟體那樣識別相同的檔案,來避免重複計算。
並行任務類程式碼:
private static class Task implements Callable<ResultItems> {
private final List<AbstractHandler> handlers;
private final File file;
public Task(List<AbstractHandler> handlers, File file) {
this.handlers = handlers;
this.file = file;
}
@Override
public ResultItems call() throws Exception {
ResultItems items = new ResultItems();
for (AbstractHandler handler : handlers) {
items.add(handler.execute(file));
}
return items;
}
}
將GUI類介面顯示與操作職責分離
為WcFrame新增操作類,分離類的職責
為GUI新增取消操作,並提高GUI介面的響應性
為WcFrame新增一個取消按鈕,並將計算操作移到其他執行緒中,提高GUI的頁面響應性
WcFrameOper中計算並設定結果的程式碼:
public void calculateAndSetResult() {
SwingUtilities.invokeLater(() -> frame.setBusyState());
future = exec.submit(new CalculateTask());
String result = null;
try {
result = future.get();
} catch (InterruptedException e) {
result = "任務失敗";
} catch (ExecutionException e) {
result = "任務失敗";
} catch (CancellationException e) {
result = "任務取消";
}
final String finalResult = result;
SwingUtilities.invokeLater(() -> {
frame.setResult(finalResult);
frame.setLeisureState();
});
}
借鑑流的思想
有些函數語言程式設計思想會使程式碼更簡單,比如集合排序及toString():
@Override
public String toString() {
return items.stream()
.sorted(Comparator.comparingLong(ResultItem::separatorCount).thenComparing(ResultItem::getFileName)
.thenComparingInt(ResultItem::getId))
.parallel().map(ResultItem::toString).collect(Collectors.joining("\r\n"));
}
在單個檔案的處理過程中也可以基於流的思想來實現並行,這要求統一handler類的處理過程,並將字元流在多個handler之間傳遞,但受現在程式結構的限制,實現這一想法是非常困難的。好在有高層次多個檔案處理的並行,程式對CPU的利用率還算可以,但在處理單個超大檔案時就不適用了。
部分測試程式碼(使用JUnit4)
CliParser類的部分測試程式碼
@Before
public void setup() {
FileName.restore();
}
@Test()
public void testGuiFailHasOtherOption() {
String[] args = new String[] { "-x", "-w" };
try {
new CliParser().parse(args);
fail();
} catch (ParseException e) {
assertEquals(ParseExceptionMess.CLI_SYSTEM_MESS, e.getMessage());
}
}
@Test()
public void testGuiFailHasArg() {
String[] args = new String[] { "-x", "code.txt" };
try {
new CliParser().parse(args);
fail();
} catch (ParseException e) {
assertEquals(ParseExceptionMess.GUI_USE_ALONE, e.getMessage());
}
}
handler包的部分測試程式碼
@Test
public void testExecute() throws IOException {
File stopFile = new File("D:\\eclipse_n_java\\Wc\\src\\test\\java\\com\\mxh\\wc\\handler\\stopWord.txt");
Files.getInstance().setStopWordFile(stopFile);
File file = new File("D:\\eclipse_n_java\\Wc\\src\\test\\java\\com\\mxh\\wc\\handler\\testStopWords.txt");
SelectWordCountHandler handler = new SelectWordCountHandler();
assertEquals(new ResultItem(file.getPath(), Args.STOP, 20), handler.execute(file));
}
總結
個人程式設計過程中,會有很多意外的錯誤,這些錯誤大多是由於某個細節的疏忽而造成的,結對程式設計就可以很好地避免這一問題,兩人都犯同一個低階錯誤的概率很小。不得不說,我浪費了一次結對程式設計的機會。