1. 程式人生 > >系統分析與設計課程項目 WordCount 結對編程

系統分析與設計課程項目 WordCount 結對編程

實現 支持 submit 這樣的 sarg 令行 命令 odi 系統

系統分析與設計課程項目 WordCount 結對編程

作業說明

合作者:

201631084230(只有我一個人,“合作者”這個標題有些不合適了)

代碼地址:

https://gitee.com/mxhkkk/Wc/tree/complete/

本次作業的鏈接地址:

https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188

為什麽只有我一個人

個人項目完成後,我看了很多人的作業博客,也評論了很多,但始終沒有發出這樣一條評論:“我們的程序設計風格很像,結對編程我們一起吧”,確實是沒有發這樣的評論。後來才發現,團隊項目組隊因此簡單了,一個人再找一個兩人組就可以了。

PSP表格

PSP2.1PSP階段預估耗時(分鐘)實際耗時(分鐘)
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
合計470730

自審

  • 命令行參數解析,不準確,沒有錯誤提示信息。我現在意識到這是一個復雜的過程,決定使用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 結對編程