1. 程式人生 > >系統分析與設計課程專案 WordCount 結對程式設計

系統分析與設計課程專案 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));
}

總結

個人程式設計過程中,會有很多意外的錯誤,這些錯誤大多是由於某個細節的疏忽而造成的,結對程式設計就可以很好地避免這一問題,兩人都犯同一個低階錯誤的概率很小。不得不說,我浪費了一次結對程式設計的機會。