系統分析與設計課程項目 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));
}
總結
個人編程過程中,會有很多意外的錯誤,這些錯誤大多是由於某個細節的疏忽而造成的,結對編程就可以很好地避免這一問題,兩人都犯同一個低級錯誤的概率很小。不得不說,我浪費了一次結對編程的機會。
系統分析與設計課程項目 WordCount 結對編程