201871010130-周學銘 實驗三 結對項目—《D{0-1}KP 實例數據集算法實驗平台》項目報告


項目 內容
課程班級博客鏈接 18卓越班
這個作業要求鏈接 實驗三結對編程要求
我的課程學習目標 體驗軟件項目開發中的兩人合作,練習結對編程(Pair programming)。
掌握Github協作開發程序的操作方法。
這個作業在哪些方面幫助我實現學習目標 通過與結對方配合實現該項目,從而熟悉了結對編程的方法。同時也熟悉了通過Github協作開發程序的操作方法。
結對方學號-姓名 201871030110-何飛
結對方本次博客作業鏈接 結對方博客連接
本項目Github的倉庫鏈接地址 github倉庫鏈接

任務一:閱讀《現代軟件工程—構建之法》第3-4章內容,理解並掌握代碼風格規范、代碼設計規范、代碼復審、結對編程概念

1.構建之法第三章總結:

  • 軟件工程師的思維誤區:
    • 分析麻痹:一種極端情況是想弄清楚所有細節、所有依賴關系之后再動手,心理上過於悲觀,不想修復問題,出了問題都賴在相關問題上。
    • 不分主次,想解決所有依賴問題:另一種極端是過於積極,想馬上動手修復所有主要和次要的依賴問題,然后就可以“完美地”達成最初設定的目標,而不是根據現有條件找到一個“足夠好”的方案。
    • 過早優化:過早優化:既然軟件是“軟”的,那它就有很大的可塑性,可以不斷改進。放眼望去,一個復雜的軟件似乎很多模塊都可以變得更好。一個工程師在寫程序的時候,經常容易在某一個局部問題上陷進去,花大量時間對其進行優化;無視這個模塊對全局的重要性,甚至還不知道這個“全局”是怎么樣的。
    • 過早擴大化/泛化:
    • 畫扇面——調侃目標和遠景:

2.構建之法第四章總結

  • 代碼規范:代碼規范可以分為兩個部分:
    • 代碼風格規范:主要是文字上的規定。
      -原則:簡明、易讀、無二義性。
      -主要包含:縮進、行寬、括號、斷行與空白的{}行、分行、命名、下划線、大小寫、注釋等。
    • 代碼設計規范:牽涉到程序設計、模塊之間的關系、設計模式等方方面面的通用原則。
      • 這部分主要描述了錯誤處理:參數的處理、斷言以及如何處理C++的類。
  • 代碼復審
    • 代碼復審的定義:看代碼是否在代碼規范的框架內正確地解決了問題。
    • 形式
名稱 形式 目的
自我復審 自己vs自己 用同伴復審的標准來要求自己。不一-定最有效,因為開發者對自己總是過於自信。如果能持之以恆,則對個人有很大好處
同伴復審 復審者vs開發者 簡便易行
團隊復審 團隊vs開發者 有比較嚴格的規定和流程,適用於關鍵的代碼,以及復審后不再更新的代碼覆蓋率高一有很多雙眼睛盯着程序,但效率可能不高( 全體人員都要到會)

  • 目的
    • 找出代碼的錯誤。
    • 發現邏輯錯誤。
    • 發現算法錯誤。
    • 發現潛在的錯誤和回歸性錯誤。
    • 發現可能需要改進的錯誤。
    • 教育(互相教育)開發人員,傳授經驗,讓更多的成員熟悉項目各部分的代碼,同時熟悉和應用領域相關的實際知識。
  • 步驟
    • 代碼必須成功地編譯。
    • 程序員必須測試過代碼。
    • 程序員必須提供新的代碼,以及文件差異分析工具。
    • 在面對面的復審中,一般是開發者控制流程,講述修改的前因后果。但是復審者有權在任何時候打斷敘述,提出自己的意見。
    • 復審者必須逐一提供反饋意見。
    • 開發者必須負責讓所有的問題都得到滿意的解釋或解答,或者在TFS中創建新的工作項以確保這些問題會得到處理。
    • 對於復審的結果,雙方必須達成一致的意見。
  • 代碼復審的核查表
    • 主要包括:概要部分,設計規范部分,代碼規范部分,具體代碼部分,效能,可讀性,可測試性。
  • 結對編程
    • 好處:1.在開發層次,結對編程能提供更好的設計質量和代碼質量,兩人合作解決問題的能力更強。兩人合作,還有相互激勵的作用,工程師看到別人的思路和技能,得到實時的講解,受到激勵,從而努力提高自己的水平,提出更多創意。
      2.對開發人員自身來說,結對工作能帶來更多的信心,高質量的產出能帶來更高的滿足感。
      3.在企業管理層次上,結對能更有效地交流,相互學習和傳遞經驗,分享知識,能更好地應對人員流動。
    • 兩人合作的各個階段:萌芽階段 → 磨合階段 → 規范階段 → 創造階段 → 解體階段。
    • 影響他人的方式:斷言,橋梁,說服,吸引。
    • 正確地給予反饋:“三明治”方法
      • 先放一片面包:做好鋪墊:強調雙方的共同點,從團隊共同的願景講起,讓對方覺得處於一個安全的環境。
      • 然后再把肉放上:這時就可以把建設性的意見( Constructive Feedback )加工好,加上生菜、佐料等。
      • 然后再來一片面包:呼應開頭,鼓勵對方把工作做好。

任務二:兩兩自由結對,對結對方《實驗二 軟件工程個人項目》的項目成果進行評價。

  • 結對方信息:
結對方學號-姓名 201871030110-何飛
結對方博客 實驗二:博客
結對方github地址 實驗二:項目地址

  • 分工:
姓名 工作任務
何飛 前端的設計與實現
周學銘 后端及算法的實現

  • 博客點評:
博客地址 實驗二:博客
博文結構 博文結構清晰,版式整潔,錯落有致,具有段落感。同時將一些重要的地方用高亮顯示了出來,具有觀看體驗。
博文內容 對於博文內容,這里按每個任務模塊進行點評。
任務1:優點是對於每個點評給出了超鏈接,且定位到了該評論的位置,極為方便;缺點是最好能在超鏈接前標注一下點評對象的姓名等信息。
任務2:最好能夠將《 構建之法》PSP那兩章進行提煉概括。
任務3:在需求分析上,對0-1背包給出了自己的解釋,相信博主一定對該問題有了較為深入的理解。同時對於任務三功能本身,博主完成度極高,而且用了GUI人機交互界面,具有很好的用戶體驗,完成的極為出色。但缺點在於回溯算法沒有實現。
博文結構與PSP中“任務內容”列的關系 博主的博文撰寫流程是按照PSP的主要流程一步一步走下來的,具有較好的整體構思。
PSP數據的差異化分析與原因探究 博主實際完成時間遠大於計划完成時間,主要是在具體編碼,代碼復審與事后總結這三個階段。結合我自己的開發經驗,主要原因可能在於對PSP具體流程的不熟悉,自我估計不足等。也有可能是在開發過程中遇到了一些預料之外的突發情況等
  • 代碼核查表:
說明 內容
概要部分
代碼符合需求和規格說明么? 基本符合,但部分功能完成度較差。
代碼設計是否考慮周全? 考慮周全
代碼可讀性如何? 采用模塊化編程,代碼可讀性好。
代碼容易維護么? 對於不同的功能模塊,分別存儲在不同的類中,維護較為容易。
代碼的每一行都執行並檢查過了嗎? 是的
設計規范部分
設計是否遵從已知的設計模式或項目中常用的模式? 采用了抽象工廠設計模式(對象創建型模式)
有沒有硬編碼或字符串/數字等存在? 沒有,采用的都是符合命名規范的變量名
代碼有沒有依賴於某一平台,是否會影響將來的移植? 沒有
開發者新寫的代碼能否用已有的Library/SDK/Framework中的功能實現? 能調用
有沒有無用的代碼可以清除? 沒有
代碼規范部分
修改的部分符合代碼標准和風格嗎? 符合規范
具體代碼部分
有沒有對錯誤進行處理?對於調用的外部函數,是否檢查了返回值或處理了異常? 沒有處理異常值
參數傳遞有無錯誤,字符串的長度是字節的長度還是字符(可能是單/雙字節)的長度是以0開始計數還是以1開始計數? 沒有
邊界條件是如何處理的? switch語句的default分支是如何處理的?循環有沒有可能出現死循環? 采用哨兵處理邊界;沒有使用switch語句;循環不會出現死循環。
有沒有使用斷言( Assert)來保證我們認為不變的條件真的得到滿足? 程序較為簡單,所以沒有使用斷言。
數據結構中有沒有用不到的元素?
效能
代碼的效能(Performance)如何?最壞的情況是怎樣的? 運行效率較低。
代碼中,特別是循環中是否有明顯可優化的部分(C++中反復創建類,C#中 string的操作是否能用StringBuilder來優化)?
對於系統和網絡的調用是否會超時?如何處理? 沒有對網絡的調用
可讀性
代碼可讀性如何?有沒有足夠的注釋? 關鍵語句都有注釋,可讀性高
可測試性
代碼是否需要更新或創建新的單元測試? 不需要

  • 日志數據:

  • 實驗測試:

    • 數據讀取:
    • 散點圖:
    • 排序:
    • 動態規划(小數據):
    • 動態規划(大數據):

任務三、任務四

  • PSP流程:

    PSP2.1 任務內容 計划完成需要的時間(min) 實際完成需要的時間(min)
    Planning 計划 60 60
    · Estimate · 估計這個任務需要多少時間,並規划大致工作步驟 60 60
    Development 開發 2340 2580
    · Analysis · 需求分析 (包括學習新技術) 60 60
    · Design Spec · 生成設計文檔 30 30
    · Design Review · 設計復審 (和同事審核設計文檔) 60 60
    · Coding Standard · 代碼規范 (為目前的開發制定合適的規范) 30 30
    · Design · 具體設計 60 60
    · Coding · 具體編碼 1800 2040
    · Code Review · 代碼復審 180 120
    · Test · 測試(自我測試,修改代碼,提交修改) 120 180
    Reporting 報告 210 210
    · Test Report · 測試報告 60 60
    · Size Measurement · 計算工作量 30 30
    · Postmortem & Process Improvement Plan · 事后總結 ,並提出過程改進計划 120 120
  • 需求分析:

    • 平台基礎功能:實驗二中的任務3
    • D{0-1}KP實例數據集存儲到數據庫:
    • 平台可動態嵌入任何一個有效的D{0-1}KP 實例求解算法,並保存算法實驗日志數據:
    • 人機交互界面要求為GUI界面:
    • 設計遺傳算法求解D{0-1}KP:
  • 軟件設計說明:

    • GUI界面:
      • 前端界面:主要采用Springboot+layui。
        • 前端的GUI使用的是layui的框架,前端界面的顯示是通過layui提供的模板,以及themeleaf、js等要素構成。
          • themeleaf提供了強大的模板支持,本次實驗中,themeleaf出演的角色是抽取公共的核心的DOM元素,在其它的前端頁面通過調用的方式進行插入DOM節點,大大節約了開發的成本,提高了代碼的可重用性;而layui主要是提供各種各樣的優化的按鈕、文本等之類的組件。
          • layui是國人構建的一個前端框架,由於開發者是做后端的程序員,所以,layui提供的接口是與后端進行了無縫銜接,同時,它通過簡單的定制,便可使得頁面渲染的效果極佳。
          • layui前端元素的顯示是通過設置其class屬性,達到顯示元素的效果較為美觀,通過layui封裝的js,即可實現數據的異步傳輸(常見的網絡通信有AJAX、axios、表單等,其中,前兩項屬於異步通信),它接收的服務器返回的參數必須是JSON格式,因此,它的js文件可能封裝了AJAX,它要求后端返回的json數據必須是數組格式(key為data)、數據的長度(key為count)、數據的額外信息(key為msg)、數據成功與否(key為code),數據域(data為json對象或者hashmap)。layui的封裝的js用於請求后台的數據,並顯示在界面上(layui是通過模塊化加載的,因此,需要引入響應的模塊)。
        • 本次項目的前端需要引用的js文件是通過cdn去加載的,因此,項目的運行需要在網絡允許的范圍內才能使用。
      • 后台功能:主要包括路由轉發以及數據庫的操作。
        • 路由的實現:主要是通過controller進行處理和轉發,只需要將路徑寫對即可。
        • 數據的加載與顯示:主要是通過從數據庫中獲取數據,構造json對象,返回到前端即可,需要注意的是,所有的服務器返回的數據必須是鍵值對的形式,最好是json(json只是鍵值對的一種,比如hashmap也是鍵值對的形式)
    • 遺傳算法:
      • 算法介紹:是模擬達爾文生物進化論的自然選擇和遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜索最優解的方法。該算法通過數學的方式,利用計算機仿真運算,將問題的求解過程轉換成類似生物進化中的染色體基因的交叉、變異等過程。是一種近似算法。
      • 算法流程:
        • 種群初始化:通過隨機函數為初始種群賦初值。即用一長串的01串來表示種群的選擇,1:選了該組數據;0:不選該組數據,所以我們可以生成一個隨機數,然后對它進行去模來進行初始化。
        • 個體評價:計算初始化種群適應度,首先對每個染色體其初值進行遍歷,因為這里是按組為單位的,每組數據下面還有三種選擇,所以需要對這三種選擇繼續進行隨機選擇。需要注意的是因為這三種選擇概率不同,所以對它們賦值的概率也不同。同時在進行選擇的時候,還要對背包的約束條件進行判斷,即是否超出了背包初始的容積。最后返回當前的最優值並累積種群中各個個體的累積概率。
        • 選擇運算:這里使用的是賭輪選擇策略,其依據是上一步中各種群中各個個體的累積概率。
        • 交叉運算:這里仍然需要使用隨機數,使用的方法為:兩點交叉算子。
        • 變異運算:依舊需要隨機函數,但使用的方法為:多次對換變異算子。
  • 項目測試:

    • GUI界面:

    • 數據庫:

      • SQL語句:
      • 基本表:
      • 日志文件:
      • 數據集有效信息:
    • 實驗二任務3的移植:

      • 數據集的上傳:


      • 數據集的查看:

      • 散點圖:

      • 數據排序:

      • 動態規划算法:


      • 回溯算法(-1表示數據量過大,無法在短時間內完成):

    • 遺傳算法:

      • 本地測試:
        • "id-kp1-10.txt"數據集的第0組數據
          • 初始化種群:
          • 迭代后的種群:
          • 最優解求解(其中最佳編碼為最優解,從前往后分別為各組數據,二分量中前一個表示1:選擇該組數據,0:不選該組數據;后一個表示選擇了該組數據的哪一個變量)
          • 數據集:
          • 答案驗證:價值:1329+998+1657+1375+971+815+1360+1948=10453
            重量:1321+1098+1781+1362+903+894+1241+1545=10145<10149
        • "id-kp1-10.txt"數據集的第1組數據

          動態規划計算出來的最優解為70106,誤差值為791,誤差值為1%,准確率可以接受。
      • web測試:


    • 日志:

  • 提交記錄

  • 軟件實現及核心功能代碼展示:

    • BacktrackProgramming.class:完成了回溯算法。
private void dfs(int x){
    back_count++;
    if(back_count>INF){
        res=-1;
        return ;
    }
    if(x>=row) {
        return ;
    }
    else {
        if(weight[x+1][2]<=back_weight) {
            back_weight-=weight[x+1][2];
            back_value+=value[x+1][2];
            if(res<back_value) {
                res=back_value;
            }
            dfs(x+1);
            back_weight+=weight[x+1][2];
            back_value-=value[x+1][2];
        }
        dfs(x+1);
        if(weight[x+1][0]<=back_weight) {
            back_weight-=weight[x+1][0];
            back_value+=value[x+1][0];
            dfs(x+1);
            if(res<back_value) {
                res=back_value;
            }
            back_weight+=weight[x+1][0];
            back_value-=value[x+1][0];
        }
        if(weight[x+1][1]<=back_weight) {
            back_weight-=weight[x+1][1];
            back_value+=value[x+1][1];
            if(res<back_value) {
                res=back_value;
            }
            dfs(x+1);
            back_weight+=weight[x+1][1];
            back_value-=value[x+1][1];
        }
    }
}
  • DynamicProgramming.class:完成了動態規划算法。
private Long knapSack(int[] weight, int[] profit, int C)
{
    int n = profit.length/3;//profit一定是3的倍數
    int[][] maxvalue = new int[n + 1][C + 1];//價值矩陣
    long before=System.currentTimeMillis();
    for (int i = 0; i < maxvalue.length; i++) {
        maxvalue[i][0] = 0;
    }
    for (int i = 0; i < maxvalue[0].length; i++) {
        maxvalue[0][i] = 0;
    }
    for (int i = 1; i < maxvalue.length; i++) {//不處理第一行
        for (int j = 1; j <maxvalue[0].length; j++) {//不處理第一列
            //處理每一個項集
            int index=(i-1)*3;//計算當前的索引值,這里以項集為單位進行計算
            ArrayList<Integer> item=new ArrayList<>();
            if (j<weight[index]&&j<weight[index+1]&&j<weight[index+2])
            {
                maxvalue[i][j]=maxvalue[i-1][j];
                continue;
            }
            if(j>=weight[index])
                item.add(Math.max(maxvalue[i-1][j],profit[index]+maxvalue[i-1][j-weight[index]]));
            if(j>=weight[index+1])
                item.add(Math.max(maxvalue[i-1][j],profit[index+1]+maxvalue[i-1][j-weight[index+1]]));
            if(j>=weight[index+2])
                item.add(Math.max(maxvalue[i-1][j],profit[index+2]+maxvalue[i-1][j-weight[index+2]]));

            item.sort((Integer o1, Integer o2)->{
                if (o1>o2) return -1;
                else if (o1==o2) return 0;
                else return 1;
            });
            maxvalue[i][j]=item.get(0);
        }
    }
    long after=System.currentTimeMillis();
    this.setOptimalSolution(maxvalue[n][C]);
    return (after-before);
}
  • GeneticProgramming.class:完成了遺傳算法。
// 初始化種群
private void initGroup() {
    int k, i;
    for (k = 0; k < scale; k++)// 種群數
    {
        // 01編碼
        for (i = 0; i < LL; i++) {
            oldPopulation[k][i] = random.nextInt(65535) % 2;
        }
    }
}

private best_one evaluate(int[] chromosome) {
    // 010110
    int vv = 0;
    int bb = 0;
    int str[]=new int[LL];
    // 染色體,起始城市,城市1,城市2...城市n
    for (int i = 0; i < LL; i++) {
        if (chromosome[i] == 1) {
            int temp=random.nextInt(65535) % 64;
            if(temp<2) {
                vv += v[i][temp];
                bb += b[i][temp];
                str[i] = temp + 1;
            }
            else{
                vv += v[i][2];
                bb += b[i][2];
                str[i] = 3;
            }
        }
        else {
            str[i]=0;
        }
    }
    if (bb > pb) {
        // 超出背包體積
        best_one x =new best_one();
        x.x=0;x.y=str;
        return x;
    } else {
        best_one x =new best_one();
        x.x=vv;x.y=str;
        return x;
    }
}

// 計算種群中各個個體的累積概率,前提是已經計算出各個個體的適應度fitness[max],作為賭輪選擇策略一部分,Pi[max]
private void countRate() {
    int k;
    double sumFitness = 0;// 適應度總和

    int[] tempf = new int[scale];

    for (k = 0; k < scale; k++) {
        tempf[k] = fitness[k];
        sumFitness += tempf[k];
    }

    Pi[0] = (float) (tempf[0] / sumFitness);
    for (k = 1; k < scale; k++) {
        Pi[k] = (float) (tempf[k] / sumFitness + Pi[k - 1]);
    }
}

// 挑選某代種群中適應度最高的個體,直接復制到子代中
// 前提是已經計算出各個個體的適應度Fitness[max]
private void selectBestGh() {
    int k, i, maxid;
    int maxevaluation;
    int max_str[] = null;

    maxid = 0;
    maxevaluation = fitness[0];
    for (k = 1; k < scale; k++) {
        if (maxevaluation < fitness[k]) {
            maxevaluation = fitness[k];
            max_str=fitness_str[k];
            maxid = k;
        }
    }

    if (bestLength < maxevaluation) {
        bestLength = maxevaluation;
        best_str=max_str;
        bestT = t;// 最好的染色體出現的代數;
        for (i = 0; i < LL; i++) {
            bestTour[i] = oldPopulation[maxid][i];
        }
    }

    // 復制染色體,k表示新染色體在種群中的位置,kk表示舊的染色體在種群中的位置
    copyGh(0, maxid);// 將當代種群中適應度最高的染色體k復制到新種群中,排在第一位0
}

// 賭輪選擇策略挑選
private void select() {
    int k, i, selectId;
    float ran1;
    for (k = 1; k < scale; k++) {
        ran1 = (float) (random.nextInt(65535) % 1000 / 1000.0);
        // System.out.println("概率"+ran1);
        // 產生方式
        for (i = 0; i < scale; i++) {
            if (ran1 <= Pi[i]) {
                break;
            }
        }
        selectId = i;
        copyGh(k, selectId);
    }
}

private void evolution() {
    int k;
    // 挑選某代種群中適應度最高的個體
    selectBestGh();
    // 賭輪選擇策略挑選scale-1個下一代個體
    select();
    float r;

    // 交叉方法
    for (k = 0; k < scale; k = k + 2) {
        r = random.nextFloat();// /產生概率
        // System.out.println("交叉率..." + r);
        if (r < Pc) {
            // System.out.println(k + "與" + k + 1 + "進行交叉...");
            OXCross(k, k + 1);// 進行交叉
        } else {
            r = random.nextFloat();// /產生概率
            // System.out.println("變異率1..." + r);
            // 變異
            if (r < Pm) {
                // System.out.println(k + "變異...");
                OnCVariation(k);
            }
            r = random.nextFloat();// /產生概率
            // System.out.println("變異率2..." + r);
            // 變異
            if (r < Pm) {
                // System.out.println(k + 1 + "變異...");
                OnCVariation(k + 1);
            }
        }

    }

}


// 兩點交叉算子
private void OXCross(int k1, int k2) {
    int i, j, flag;
    int ran1, ran2, temp = 0;

    ran1 = random.nextInt(65535) % LL;
    ran2 = random.nextInt(65535) % LL;

    while (ran1 == ran2) {
        ran2 = random.nextInt(65535) % LL;
    }
    if (ran1 > ran2)// 確保ran1<ran2
    {
        temp = ran1;
        ran1 = ran2;
        ran2 = temp;
    }
    flag = ran2 - ran1 + 1;// 個數
    for (i = 0, j = ran1; i < flag; i++, j++) {
        temp = newPopulation[k1][j];
        newPopulation[k1][j] = newPopulation[k2][j];
        newPopulation[k2][j] = temp;
    }

}

// 多次對換變異算子
private void OnCVariation(int k) {
    int ran1, ran2, temp;
    int count;// 對換次數
    count = random.nextInt(65535) % LL;

    for (int i = 0; i < count; i++) {

        ran1 = random.nextInt(65535) % LL;
        ran2 = random.nextInt(65535) % LL;
        while (ran1 == ran2) {
            ran2 = random.nextInt(65535) % LL;
        }
        temp = newPopulation[k][ran1];
        newPopulation[k][ran1] = newPopulation[k][ran2];
        newPopulation[k][ran2] = temp;
    }
}

private void solve() {
    int i;
    int k;

    // 初始化種群
    initGroup();
    // 計算初始化種群適應度,Fitness[max]
    for (k = 0; k < scale; k++) {
        best_one temp= evaluate(oldPopulation[k]);
        fitness[k]=temp.x;
        fitness_str[k]=temp.y;
    }

    // 計算初始化種群中各個個體的累積概率,Pi[max]
    countRate();

    for (t = 0; t < MAX_GEN; t++) {
        evolution();
        // 將新種群newGroup復制到舊種群oldGroup中,准備下一代進化
        for (k = 0; k < scale; k++) {
            for (i = 0; i < LL; i++) {
                oldPopulation[k][i] = newPopulation[k][i];
            }
        }
        // 計算種群適應度
        for (k = 0; k < scale; k++) {
            best_one temp= evaluate(oldPopulation[k]);
            fitness[k]=temp.x;
            fitness_str[k]=temp.y;
        }
        // 計算種群中各個個體的累積概率
        countRate();
    }
}
  • InitAnalysis.class:文件讀取存儲模塊。
public boolean getAllParameterByFile(String filePath) throws IOException {
    //若目錄不存在,則創建目錄
    File file=new File(filePath);
    String data= FileUtils.readFileToString(file,"UTF-8");
    //初步格式化件
    String getTextByDeleteWhiteSpace=StringUtils.deleteWhitespace(data);//刪除所有的空格
    if (getTextByDeleteWhiteSpace.charAt(2)=='*') {
        int getFirstDKPIndex = getTextByDeleteWhiteSpace.indexOf("DKP");//獲得首次出現DKP的位置
        String formatPrefixText = getTextByDeleteWhiteSpace.substring(getFirstDKPIndex + 3);//刪除冒號以前的字符
        int getLastFullIndex = formatPrefixText.lastIndexOf('.');//獲得最后一次出現.的位置
        int getLastCommaIndex = formatPrefixText.lastIndexOf(',');//獲得最后一次出現,的位置
        int index = getLastFullIndex > getLastCommaIndex ? getLastFullIndex : getLastCommaIndex;//獲得格式化后的文本
        String formatSuffixText = formatPrefixText.substring(0, index);
        getProfitAndWeight(formatSuffixText);//獲取profit和weight
        return true;
    }else{
        return false;
    }
}
  • JDBCUtils.class:數據庫連接模塊。

  • 結對的過程

    • 首先我們對項目進行了詳細的分工,並對項目使用的框架及架構進行了探討。
    • 其次對項目的布局進行了設計。
    • 之后我們各自完成了其負責的部分,並在qq等平台上進行聯系與溝通。









  • 總結:

    • 在組員積極交流且按照極限編程的方式進行項目開發是可以帶來1+1>2的效果。
    • 結對編程要特別注意代碼的規范問題,以及數組的下標。(比如我原本的程序,數組的下標是從1開始的;而我的結對方其數值的下標是從0開始的。)
    • 結對編程時要多保持交流,積極反饋當前遇到的問題與解決措施。
    • 但如果兩個人在一起工作時,其中一個人想偷懶去干別的,那么就會拖延工作進度。
    • 兩人互相監督工作,可以增強代碼和產品質量,並有效的減少BUG。
    • 在編程中,相互討論,可以更快更有效地解決問題,互相請教對方,可以得到能力上的互補。
    • 由於時間緊張,web界面還沒有將解向量的數據輸出到前端界面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM