天天看點

軟工實踐寒假作業(2/2)

這個作業屬于哪個課程 2019學年02學期單紅老師軟體工程實踐
這個作業要求在哪裡 寒假作業(2/2)連結
這個作業的目标 開發一個簡易的疫情統計程式
作業正文 本博文
其他參考文獻 CSDN相關部落格、建構之法

GitHub倉庫位址

Benjamin_Gnep疫情統計

PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 30
Estimate 估計這個任務需要多少時間 1890 2250
Development 開發 960 1170
Analysis 需求分析 (包括學習新技術) 390 410
Design Spec 生成設計文檔 90 120
Design Review 設計複審 10
Coding Standard 代碼規範 (為目前的開發制定合适的規範) 150
Design 具體設計
Coding 具體編碼 480 540
Code Review 代碼複審 60
Test 測試(自我測試,修改代碼,送出修改) 330 460
Reporting 報告
Test Repor 測試報告 20
Size Measurement 計算工作量
Postmortem & Process Improvement Plan 事後總結, 并提出過程改進計劃
合計

需求分析

  1. 分析外部傳遞的參數并進行不同的資料處理
  2. 檔案的讀取和寫入
  3. 具有可拓展性和較強的可讀性、封裝性等
  4. 檔案的編碼确認
  5. 字元串的格式處理以及正規表達式的使用
  6. 檔案的合法性确認

設計思路

  1. 指令分析使用正規表達式判斷參數和值并且使用map儲存
  2. 先讀入檔案清單,然後通過date參數進行日志篩選
  3. 将每個檔案的每行連接配接在一起,然後組成一個待處理文本集進行處理
  4. 使用責任鍊模式對每行文本進行處理
  5. province和type可以傳回連結清單,若不存在與表中的元素不予輸出
  6. 建構一個TxtTool儲存檔案讀入等一系列操作
  7. 建構一個CmdArgs對讀入的參數與值進行處理
  8. 省份、類型以及參數使用枚舉類存儲,友善拓展
  9. 針對上述知識點到相關部落格進行學習和查找

設計實作過程

軟工實踐寒假作業(2/2)

代碼說明

核心方法-excute

execute代碼存在兩個變量,分别是分析province傳回的省份字元串以及分析type傳回的typeList,主要執行十分簡單,因為ListKey存在編号,是以for循環執行的時候會以log->date->province->type->out的順序執行,友善确認list參數個數以及拓展添加。
@Override
public void execute(Map<String,List<String>>map) {
    String proString = null;
    List<String> typeString = new LinkedList<String>();
    for(int i = 0; i < ListKey.values().length ;i++) {
        listKey = ListKey.valueOf(i);
        switch(listKey) {
            case DATE:
                dateKey(map);
                result = DataManager.solveData(logLine);
                DataManager.mergeData(result);
                break;
            case LOG:
                logKey(map);
                break;
            case OUT:
                outKey(map,proString,typeString);
                break;
            case TYPE:
                typeString = typeKey(map);
                break;
            case PROVINCE:
                proString = provinceKey(map);
                break;
        }
    }
}
           

枚舉類舉例-ProvinceValue

枚舉類可以很友善的進行資料的存儲編号,在province枚舉類中還可以通過編号的方式事先确定好排序,枚舉類不僅可以在switch中使用,還可以非常友善地進行複用。通過map進行索引還可以由鍵找值,由值尋鍵。
enum ProvinceValue{
    China(0,"全國"), Anhui(1,"安徽"),Aomen(2,"澳門"), Beijing(3,"北京")
    ,Chongqin(4,"重慶"), Fujian(5,"福建"),Gansu(6,"甘肅"), Guangdong(7,"廣東")
    ,Guangxi(8,"廣西"), Guizhou(9,"貴州"), Hainan(10,"海南"), Hebei(11,"河北")
    ,Henan(12,"河南"), Heilongjiang(13,"黑龍江"),Hubei(14,"湖北"), Hunan(15,"湖南")
    ,Jiangsu(16,"江蘇"), Jiangxi(17,"江西"), Jilin(18,"吉林"), Liaoning(19,"遼甯")
    ,Neimenggu(20,"内蒙古"), Ningxia(21,"甯夏"),Qinghai(22,"青海")
    ,Shandong(23,"山東"),Shanxi(24,"山西"),ShanXi(25,"陝西"),Shanghai(26,"上海")
    ,Sichuan(27,"四川"),Taiwan(28,"台灣"),Tianjin(29,"天津"),Xizang(30,"西藏")
    ,Xinjiang(31,"新疆"),Xianggang(32,"香港"), Yunnan(33,"雲南"),Zhejiang(34,"浙江");
    private int key;
    private String text;
    private ProvinceValue(int key,String text){
        this.key = key;
        this.text = text;
    }

    private static HashMap<Integer,String> map = new HashMap<Integer,String>();
    static {
        for(ProvinceValue d : ProvinceValue.values()){
            map.put(d.key,d.text);
        }
    }

    public static int keyOfProvince(String string) {
        for (Entry<Integer, String> entry : map.entrySet()) {
                if (string.contains(entry.getValue())) {
                    return entry.getKey();
                }
        }
        return -1;
    }

    public static ProvinceValue valueOf(int ordinal) {
        if (ordinal < 0 || ordinal >= values().length) {
            throw new IndexOutOfBoundsException("Invalid ordinal");
        }
        return values()[ordinal];
    }

    String getText() {
        return text;
    }

    int getKey() {
        return key;
    }
}
           

資料存儲-Map(String,List)

在類與類之間需要由資料結構進行資料傳遞,使用hashmap可以保證安全性的同時,将多種資料結構集合在一起,并且可以一一對應,不會造成一值多鍵的情況。分析指令行時,以‘-’作為識别參數的方式,再将參數後到另一參數前的值全部存取到list中,這樣可以解決多值參數的問題,同時也確定每一個單詞都被擷取。
public Map<String, List<String>> fillMap(Map<String, List<String>> map) {
        String key;
        List<String> value;
        while(index < args.length) {
            key = argKey();
            key = key.toLowerCase().trim();
            value = argVals();
            map.put(key, value);
        }
        return map;
}

String argKey() {
    if(index<args.length && args[index].matches(Constant.COMMAND_REG)) {
        //index 作為現在處理到的位置
        String[] key = args[index].split(Constant.SPILT_COMMAND);
        index++;
        //System.out.println(key[1]);
        return key[1];
    }
    return null;
}

List<String> argVals() {
    List<String> values = new LinkedList<String>();
    while(index<args.length && (!args[index].matches(Constant.COMMAND_REG))) {
        //将參數所有的值放入values中
        values.add(args[index]);
        index++;
    }
    Iterator<String> it = values.iterator();
    if(!it.hasNext()) {
        values.add(Constant.DEFAULT);
    }
    return values;
}
           

責任鍊模式-MyHandler

責任鍊模式可以很友善地拓展需求,一個類的修改不會影響另外一個類,利用Handler虛拟類,衍生出所有處理各類情況的類,確定可以處理每一種情況的文本,同時也減少了程式的耦合性,友善閱讀。
class DataManager{
    public static List<int[]> solveData(List<String>data){
        List<int[]> result = new LinkedList<int[]>();
        ListInit(result);
        System.out.println("建立責任鍊");
        AddipHandler addip = new AddipHandler(result);
        AddSpHandler addSp = new AddSpHandler(result);
        ChangeHandler change = new ChangeHandler(result);
        CureHandler cure = new CureHandler(result);
        DeathHandler death = new DeathHandler(result);
        ExcludeHandler exclude = new ExcludeHandler(result);
        SwapIpHandler swapIp = new SwapIpHandler(result);
        SwapSpHandler swapSp = new SwapSpHandler(result);
        addip.nextHandler = addSp;
        addSp.nextHandler = change;
        change.nextHandler = cure;
        cure.nextHandler = death;
        death.nextHandler = exclude;
        exclude.nextHandler = swapIp;
        swapIp.nextHandler = swapSp;
        swapSp.nextHandler = null;
        System.out.println("建立責任鍊完成,開始處理文本");
        Iterator<String> it = data.iterator();
        while(it.hasNext()) {
            String s = it.next();
            addip.handleRequest(s);
        }
        System.out.println("---文本處理完成");
        return result;
    }
    //......
}
           

單元測試

測試字元串為下列字元串數組,輸入輸出目錄為了友善放在了學号下
軟工實踐寒假作業(2/2)
dateScreen方法作為檔案清單的篩選,可以看出,篩選掉了01-23後的日期檔案
軟工實踐寒假作業(2/2)
dateKey方法作為篩選檔案、整合檔案的方法,可以看出把所有待處理檔案(去掉注釋後)完整的連接配接在一起
軟工實踐寒假作業(2/2)
輸出到檔案的内容經過驗證無誤
軟工實踐寒假作業(2/2)

覆寫率以及性能

作為主要方法的ListCommand,除了各個方法内必要的空值判定外,其他代碼均已覆寫,核心方法execute覆寫率為百分百
軟工實踐寒假作業(2/2)
責任鍊的代碼覆寫率也達到了百分之百
軟工實踐寒假作業(2/2)
部分工具類的使用情況:覆寫率也基本達到了90%以上
軟工實踐寒假作業(2/2)
以下為性能測試圖
軟工實踐寒假作業(2/2)
軟工實踐寒假作業(2/2)

代碼規範

Benjamin_Gnep代碼規範

心路曆程

  初看到這個需求,滿腦子沒有一點思路。

  的确要感謝很多同學的幫助,讓我明白了如何了解一些複雜難懂的概念,然後慢慢開始梳理怎麼開始進行編碼。

  B站真不愧是學習網站,我在上面找到了相關的入門教程,對于Git和Github都有了初步的了解,視訊教程相對于博文來講更加容易了解。

  之後通過相關博文,我就開始對設計模式的指令模式和責任鍊模式進行學習,腦袋裡有了初步的想法,包括枚舉類和一些工作類的編寫,然後整理了我的文本處理思路,便開始了初步的編寫,基本上都是邊寫邊查,遇到知識盲區就看看别人的代碼,查查别人的隻是總結,還是有很多收獲的。

  我在建構方法的時候盡量把功能細化,是以可能會導緻代碼量特别多,但是對于一些簡單的方法複用率就高一些。

  "軟體=程式+軟體工程"

  建構之法的閱讀我明白了很多代碼規範,團隊協作的關鍵在于如何讓别人讀懂你的程式,你可能可以把這個功能實作的很完美,但是沒有規範沒有注釋的代碼就是一坨狗屎。别人不可能花大量的時間收拾你的爛攤子。

Github前端相關倉庫

Front-end-Developer-Interview-Questions 面試題集合

bootstrap 架構

Front-end-Collect 優秀網站、部落格、活躍開發者

Vue 架構

Javascript 須知

  希望以後的生活一日三餐,一年四季。