【軟工】結對項目博客

項目 內容
這個做業屬於的課程是 2019BUAA軟件工程
做業要求是 結對編程做業
我在此次的目標是 體會結對編程、鍛鍊合做能力
這個做業在哪些方面幫助我實現目標 對結對編程有了更深的理解,爲多人合做打下基礎

(一)項目github地址

github地址html

(二)在開始實現程序以前,在下述PSP表格記錄下你估計將在程序的各個模塊的開發上耗費的時間

PSP2.1 Personal Software Process Stages 預估耗時(分鐘)
Planning 計劃 20
· Estimate · 估計這個任務須要多少時間 20
Development 開發 840
· Analysis · 需求分析 (包括學習新技術) 60
· Design Spec · 生成設計文檔 20
· Design Review · 設計複審 (和同事審覈設計文檔) 10
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 10
· Design · 具體設計 50
· Coding · 具體編碼 500
· Code Review · 代碼複審 40
· Test · 測試(自我測試,修改代碼,提交修改) 150
Reporting 報告 60
· Test Report · 測試報告 30
· Size Measurement · 計算工做量 10
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 20
合計 920

(三)看教科書和其它資料中關於Information Hiding, Interface Design, Loose Coupling的章節,說明大家在結對編程中是如何利用這些方法對接口進行設計的。

三個概念學習總結以下:前端

  1. Information Hiding
    信息隱藏。是指在設計和肯定模塊時,使得一個模塊內包含的特定信息(過程或數據)對於不須要這些信息的其餘模塊來講,是不可訪問的。簡單來講,就是在面向對象思想中所學習的「封裝」,用類來封裝其中的數據和方法,目的是使外界不可隨意訪問和修改,從而提供程序的安全性和健壯性。
  2. Interface Design
    接口設計。接口設計是基於模塊的。接口的實際意義在於,軟件開發是一個團隊的過程,所以必須將軟件準確的劃分紅幾部分,將分紅的幾部分交給不一樣的人來負責作,而接口就至關於與連部分之間鏈接的插槽,這樣將各個模塊組裝起來才能構成一個穩定、安全、可擴展性強的軟件。
  3. Loose Coupling
    鬆耦合。耦合性是指組件/模塊等互相依賴的程度。鬆耦合要求最小化依賴,實現可伸縮性、靈活性和容錯,耦合程度越高,模塊與模塊之間的聯繫性就更高,系統的靈活性就越低,報錯率就更高。

在本項目中,是這樣借鑑這些方法的:git

  1. 信息隱藏:在咱們的項目中,好比對於不須要也不該該暴露給外界的數據和方法,會使用private進行修飾,從而保證信息隱藏。
  2. 接口設計:因爲咱們大部分時間仍是並行工做,所以須要先將接口定義好,以便於後續的代碼集成,設計接口時,因爲博客中已經統一了最核心的兩個接口、且本次做業的複雜度不是很大,因此接口設計還算簡單。
  3. 鬆耦合:咱們項目分爲3個板塊:前端(視圖View)、計算核心(模型Model)、鏈接前兩者(控制Controller),用Qt Designer進行較爲專業、所見既所得的視圖設計,用VS開發核心的計算模塊,最後控制器做爲中間媒介鏈接視圖和模型,從前端獲取數據傳給模型計算,計算結果經過控制器傳給前端顯示。使得整個開發過程並行度更高,耦合更鬆。

(四)計算模塊接口的設計與實現過程。 設計包括代碼如何組織,好比會有幾個類,幾個函數,他們之間關係如何,關鍵函數是否須要畫出流程圖?說明你的算法的關鍵(沒必要列出源代碼),以及獨到之處。

​ 在計算模塊中,一共有4個類,包括:Input類,處理命令行輸入的類;Readin類,從文件中讀入單詞的類;Genlist類:搜索並生成單詞列表的類;Core類:封裝好的接口類。程序員

Input類有兩個函數github

函數名 函數做用 調用關係
CompareStr 用來比較讀入的字符串最後是否是」.txt「
InputHandle 用來處理命令行的輸入,修改Input類變量 CompareStr

Readin類中有四個函數算法

函數名 函數做用 調用關係
compare 重定向sort的比較函數,變成降序
compare_str 重定向sort的比較函數,改爲字符串比較
GetWords 從文件中讀取單詞,到Words裏,修改類成員變量
ClassifyWords 將已經讀入的單詞分類,創建26*26個vector,存放以某個字母開頭以某個字母結尾的全部單詞,並將他們排序 compare、compare_str

GenList類中有十個函數編程

函數名 函數做用 調用關係
PrintRoad 輸出搜到的路徑,路徑是有環的時候搜到的
ClassifyWords 單詞分類,將已經讀入的單詞分類,創建26*26個vector,存放以某個字母開頭以某個字母結尾的全部單詞,並將他們排序
SerchList 在已經排過序的單詞中查找單詞鏈,適用於無環模式,運用深度優先搜索的方式,而且採用剪枝
SerchCircle 查找單詞列表中有沒有環,若是不是-r模式,有環須要報錯
SerchListCircle 有環的模式下查找單詞鏈,運用深度優先搜索的方式
Print 輸出查找到的單詞鏈,單詞鏈是無環模式下找到的
SerchListLastMode 規定結束字母而且是無環模式下找到單詞鏈
gen_chain_word 模式是-w的時候,按照傳入的各個參數查找單詞鏈,存到result裏,返回單詞鏈長度 PrintRoad、ClassifyWords、SerchList、SerchCircle、SerchListCircle、Print、SerchListLastMode、GetOutDeg
gen_chain_char 模式是-c的時候,按照傳入的各個參數查找單詞鏈,存到result裏,返回單詞鏈長度 PrintRoad、ClassifyWords、SerchList、SerchCircle、SerchListCircle、Print、SerchListLastMode、GetOutDeg
GetOutDeg 獲得全部字母的出度

Core類中有兩個函數:數組

函數名 函數做用 調用關係
gen_chain_word 實例化GenList類,調用GenList類中gen_chain_word,實現封裝 GenList->gen_chain_word
gen_chain_char 實例化GenList類,調用GenList類中gen_chain_char,實現封裝 GenList->gen_chain_char

使用的時候,首先調用Input類中的InputHandle來處理命令行參數,處理完後,這個類中的變量已經被修改,以後拿到修改後的變量來調用Readin類中的GetWords函數,獲得單詞數組,以後實例化Core類,根據Input類中的類變量來肯定調用Core中的哪個函數,若是是-w模式就調用gen_chain_word函數,若是是-c模式就調用gen_chain_char函數。安全

算法有兩部分,一部分是不能夠存在環的,另外一部分是能夠存在環的。函數

​不能夠存在環的部分,主要算法是深度優先搜索,算法把每一個字母做爲圖的一個節點,由於不存在環,因此從一個字母到一個字母只能走一遍,因此能夠忽略相同開始和結束字母的全部單詞。以後進行深度優先搜索,在搜索的過程當中,進行剪枝的操做,當一個點已經走完以後,這個點到結尾的最大深度已經能夠知道,這個時候咱們記錄下來這個最大深度,若是這個點出度爲0,那麼這個點的最大深度爲0,在進入遞歸前,判斷這個點有沒有被走過,若是被走過了,就不進入這個點的遞歸,這樣就減小了不少進入遞歸的次數,從而使速度變快。

​這個算法的獨到之處在於這個算法減小了遞歸的次數,作到了一個剪枝的操做,而且以後記錄下來了最大的長度,至關於每一個邊最多隻走一遍,也就是算法的最大複雜度爲26*26,相似於動態規劃的速度,而且代碼從深度優先搜索上的基礎而來,代碼也比較好寫易懂。

​對於存在環的部分,使用深度優先搜索,由於能夠存在環,因此以單詞做爲節點,進行了部分剪枝,當一個當前最大路徑中存在一個字母,從這個字母開始的全部邊都走過,那麼這個時候,就不須要以這個點爲起點開始搜路,由於以這個點開始的全部點都被走過,路徑的最長長度也小於這個如今的最長長度,通過這個剪枝操做,可使得速度快不少。

​在查找與當前節點相連的邊的時候,記錄下了那些字母可能與這個點相連,這樣遍歷的時候就不須要遍歷26個字母,加快了查找的速度。進行了剪枝操做,使得速度變快,總體代碼思路也是從深度優先搜索開始設計,因此代碼比較好寫易懂。

​查找環的部分採用拓撲排序的辦法。首先查找收尾字母同樣的單詞序列,若是長度大於兩個,那麼就成環,以後,按照單詞的收尾字母來初始化每一個字母的入度,從入度爲0的點開始刪掉,而且與它相連的點的入度減一,重複這個操做直到沒有點能夠被刪掉,最後遍歷每一個點的入度,若是有大於0的點,那麼說明成環。

​採用拓撲排序的方式,避免了深度優先搜索的速度慢,而且代碼簡潔效率高。

(五)閱讀有關UML的內容。畫出UML圖顯示計算模塊部分各個實體之間的關係(畫一個圖便可)。

UML使用VS繪製,​類圖畫法分享以下:在VS2017的安裝包內,安裝其餘工具中的類設計器,以後進入vs2017,找到須要查看的項目,右鍵項目點擊查看會找到類圖,以後能夠看到每一個類的具體信息,以後本身修改添加調用信息。

UML圖以下:

(六)計算模塊接口部分的性能改進。記錄在改進計算模塊性能上所花費的時間,描述你改進的思路,並展現一張性能分析圖(由VS 2015/2017的性能分析工具自動生成),並展現你程序中消耗最大的函數。

​在改進計算模塊上,一共花費了大約12小時,主要改進了算法在無環的時候的搜索速度,程序最開始的時候,計算無環的算法是無任何剪枝的深度優先搜索,這樣,最慢的狀況是26!,顯然,這樣搜索的速度太慢,因此要進行優化,這部分,咱們主要進行了剪枝的優化。

首先我記錄了每個節點到最終節點的距離,若是這個點自己就是最終節點,那麼就記錄它的距離爲0,而且,若是這個點已經有到結尾的最大距離,那麼就不進入這個點的遞歸,也就是不進入下一層,這樣就少了不少重複遞歸的操做,走過的邊不會再次被走到,因此就使得代碼運行速度顯著提升。而且,有些計算vector的size的函數在for中被重複使用,在把這些提出來之後,代碼運行速度變高。開始,成環部分進行深度優先搜索的時候,每次進入遞歸都要把建好的單詞圖複製一份傳入下一層,這個複製操做很慢,因此改進的過程當中,把這個圖放到類變量中,就不須要每次都傳進去,這樣速度快了十倍以上,以後,思考了剪枝的方式,在當前最長路徑中若是有一個字母,它的全部後繼都被用到,這個時候,最長的單詞鏈不可能以這個字母爲開頭,因此能夠減小遍歷的次數,速度快了五倍左右。

​消耗最大的函數:

​由於算法在尋找路這一方面作的比較優秀,因此慢在單詞的分類,分類中有單詞的查重功能,在找到當前的單詞就查找一下有沒有和他重複的,這個時候,strcmp就會慢不少。

​在成環的時候,性能分析和不成環就不同。


​消耗最大的函數:

這個時候,算法主要在遞歸中,因此全部的消耗基本在進入遞歸的函數中,函數出來也消耗了大量寫入的操做。

(七)看Design by Contract, Code Contract的內容,描述這些作法的優缺點, 說明你是如何把它們融入結對做業中的。

如下內容參考自一篇優秀的博客

Design by Contract指的是契約式編程,Code Contract指的是代碼契約,兩者的主要觀點是一致的:

契約式編程,源自生活中供應者與客戶之間的「契約/合同」,契約用於兩方,每一方都期待從契約中得到利益,同時也要接受一些義務。一般,一方視爲義務的對另外一方來講是權利。契約文檔要清楚地寫明雙方的權利與義務。

一樣的道理也適用於軟件,簡而言之,就是函數調用者應該保證傳入函數的參數是符合函數的要求,若是不符合函數要求,函數將拒絕繼續執行。

在軟件體系中,程序庫和組件庫被類比爲server,而使用程序庫、組件庫的程序被視爲client。根據這種C/S關係,咱們每每對庫程序和組件的質量提出很嚴苛的要求,強迫它們承擔本不該該由它們來承擔的責任,而過度縱容client一方,甚至要求庫程序去處理明顯因爲client錯誤形成的困境。客觀上致使程序庫和組件庫的設計和編寫異常困難,並且質量隱患反而更多;同時client一方代碼大多鬆散隨意,質量低劣。這種情形,就好像在一個權責不清的企業裏,必然會養一批屍位素餐的混混,苦一批不辭辛苦,不計得失的老黃牛。引入契約觀念以後,這種C/S關係被打破,你們都是平等的,你須要我正確提供服務,那麼你必須知足我提出的條件,不然我沒有義務「排除萬難」地保證完成任務。

優勢:

  1. 權責清晰,給「異常」下了一個清晰、可行的定義:對方徹底知足了契約,而我依然未能如約完成任務。
  2. 保證軟件的可靠性、可擴展性和可複用性。

缺點:須要斷言機制來驗證契約是否成立,但並不是全部的程序語言都有斷言機制。

在本項目中,咱們在代碼中設置了一些斷言機制,代表某個函數對於傳入的參數的一些最基本的要求,好比 處理文件內容的函數GetWords會要求傳入的文件名不能爲null。

(八)計算模塊部分單元測試展現。展現出項目部分單元測試代碼,並說明測試的函數,構造測試數據的思路。並將單元測試獲得的測試覆蓋率截圖,發表在博客中。要求整體覆蓋率到90%以上,不然單元測試部分視做無效。

計算模塊部分的單元測試代碼 部分展現以下:

TEST_METHOD(TestMethod1)
        {
            // TODO: 在此輸入測試代碼
            Input *input = new Input();
            int n = 3;
            char * instr[] = { " ", "-w","..\\Wordlist\\a.txt" };
            char ** result = new char *[11000];
            int len = 0;
            input->InputHandle(n, instr);
            Readin *readin = new Readin();
            readin->GetWords(input->FileName);

            int i = 0;
            for (i = 0; i < 8; i++)
            {
                Core *core = new Core();
                switch (i)
                {
                case(0):
                    len = core->gen_chain_word(readin->Words, readin->WordNum, result, '0', '0', false);
                    Assert::AreEqual(len, 29);
                    break;
                case(1):
                    len = core->gen_chain_word(readin->Words, readin->WordNum, result, 'd', '0', false);
                    Assert::AreEqual(len, 27);
                    break;
                case(2):
                    len = core->gen_chain_word(readin->Words, readin->WordNum, result, '0', 'e', false);
                    Assert::AreEqual(len, 27);
                    break;
                case(3):
                    len = core->gen_chain_word(readin->Words, readin->WordNum, result, 'd', 'e', false);
                    Assert::AreEqual(len, 25);
                    break;
                case(4):
                    len = core->gen_chain_char(readin->Words, readin->WordNum, result, '0', '0', false);
                    Assert::AreEqual(len, 29);
                    break;
                case(5):
                    len = core->gen_chain_char(readin->Words, readin->WordNum, result, 'd', '0', false);
                    Assert::AreEqual(len, 27);
                    break;
                case(6):
                    len = core->gen_chain_char(readin->Words, readin->WordNum, result, '0', 'e', false);
                    Assert::AreEqual(len, 27);
                    break;
                case(7):
                    len = core->gen_chain_char(readin->Words, readin->WordNum, result, 'd', 'e', false);
                    Assert::AreEqual(len, 25);
                    break;
                }

                delete core;
            }

        }

計算模塊的單元測試的函數是TestCore,被測試的函數是gen_chain_char和get_chain_word。構造測試數據主要是兩個思路:

  1. 經過代碼隨機地生成一些測試輸入,而後與另一組同窗一塊兒跑這個樣例,對比輸出是否相同。
  2. 手動構造一些測試數據並計算結果,構造-w和-r測試樣例時,首先構造一個有向無環圖,而後手動求解其拓撲排序,能夠參見這裏

單元測試獲得的測試覆蓋率截圖以下,可見覆蓋率達到了90%以上:

(九)計算模塊部分異常處理說明。在博客中詳細介紹每種異常的設計目標。每種異常都要選擇一個單元測試樣例發佈在博客中,並指明錯誤對應的場景。

計算模塊部分的異常處理有兩種:

(1)按照文本內容和命令要求,不存在可行解。這包括:

  1. 不管命令爲什麼,文本內容都沒法成鏈。這樣一個單元測試的輸入能夠是:ab cd lmn opq uvw xyz ef rst g hi jk。
  2. 文本內容本能夠成鏈,但因爲首尾字母的限制而沒有可行解。這樣一個單元測試的輸入能夠是:ab bc cd de,命令參數能夠是WordList.exe -w -h d test.txt。

(2)命令中沒有-r,可是文本內容能夠成環。一個單元測試樣例的輸入是:abc cfs ehshoda sefe sewqq

這裏順便列出整個項目的全部異常種類的設計:

拋出異常的模塊 拋出異常的場景 錯誤提示語
Input(命令行參數處理模塊) -h 後緊跟着的參數不是單字符 Too Long Begin!
-t 後緊跟着的參數不是單字符 Too Long End!
-h或-t後緊跟着的參數是單字符,但不是字母 Need a alapa
文件名後還有參數 Too many parameters
沒有-w或-c need one -c or -w
不是-w -c -r -h -t,也不是以.txt結尾的文件名 illegal parameter
命令行參數中沒有文件名 No a legal file
Readin(文件內容處理模塊) 沒法打開命令行中指定的文件(好比文件不存在) Fail to open the file
文本中單詞個數過多 Too many words
單詞的長度過長 Too long word!
Core(計算核心模塊) 按照文本內容和用戶命令的要求,沒有找到解 No solution
命令中沒有-r,可是文件中出現了單詞環 Become Circle
Main(主函數) 其它異常狀況 Error

(十)界面模塊的詳細設計過程。在博客中詳細介紹界面模塊是如何設計的,並寫一些必要的代碼說明解釋實現過程。

界面模塊使用VS+Qt,這裏分享兩個安裝教程:教程1教程2

整個界面是一個QDialog(因爲是一個小軟件因此沒有選擇QMainWindow),界面模塊主要包括「視圖view」和「控制controller」兩部分:

(1)經過所見既所得的Qt Designer設計UI視圖,對佈局作了一些考慮,放一張佈局設計圖:

總體採用縱向佈局,其中最上面的部分採用水平佈局,各個部分的功能以下:

  1. 最左邊是輸入,能夠經過文件導入或手動輸入,導入文件的內容會覆蓋輸入框並容許用戶手動追加文本
  2. 中間部分的4個用來設定參數:下拉選擇框提供-w和-c兩種選擇,單選按鈕提供是否選擇-r,下面非必填的兩個輸入框用來設定首尾字母(僅能輸入一個字符,能夠選擇不輸入)。
  3. 最右邊是輸出,將計算結果顯示在輸出框中,能夠導出爲文件。

(2)經過信號與槽機制設置「哪一個組件的什麼事件會觸發哪一個類的什麼方法」。按照上述每一個組件的功能設置槽函數,並在go按鈕點擊事件對應的槽函數中調用文本處理模塊readin和計算模塊core,完成視圖、控制器、模型的對接。

比較難寫的槽函數在於文件的導入和導出,咱們參考了這篇博客使用了QFileDialog,下面給出咱們這部分的代碼:

void Dialog::on_btn_import_clicked()
{
    QString filepath = QFileDialog::getOpenFileName(this,tr("choose file"));
    if(filepath!=NULL){
        QByteArray ba = filepath.toLatin1();
        char *filepath_c;
        filepath_c = ba.data();

        ui->le_path->setText(filepath);
        FILE *fp;
        fopen_s(&fp,filepath_c,"r");

        fseek(fp,0,SEEK_END);
        int filesize = ftell(fp);
        fseek(fp,0,SEEK_SET);

        char *buf = new char[filesize+1];
        int n = fread(buf,1,filesize,fp);
        if(n>0){
            buf[n] = 0;
            ui->te_in->setText(buf);
        }
        delete[] buf;
        fclose(fp);
    }
}

void Dialog::on_btn_export_clicked()
{
    QString filename = QFileDialog::getSaveFileName(this,tr("save as"));
    QByteArray ba = filename.toLatin1();
    char *filepath_c;
    filepath_c = ba.data();

    QString text = ui->tb_out->toPlainText();
    QByteArray ba2 = text.toLatin1();
    char *text_c;
    text_c = ba2.data();

    if(filename.length()>0){
        FILE *fp;
        fopen_s(&fp, filepath_c, "w");
        fwrite(text_c,1,text.length(),fp);
    }
}

(十一)界面模塊與計算模塊的對接。詳細地描述UI模塊的設計與兩個模塊的對接,並在博客中截圖實現的功能。

前面提到,咱們在GO按鈕點擊事件對應的槽函數中 調用文本處理模塊Readin和計算模塊Core,從而完成視圖、控制器、模型的對接。

具體來講,咱們把槽函數都放到了Dialog類中,當用戶點擊GO按鈕後,會觸發Dialog類中的on_btn_go_clicked()槽函數,這個函數將視圖中文本輸入框的內容傳遞給核心控制器Calculator類,設置其成員變量textIn,而後調用Calculator類的核心函數core()進行計算,計算結果再經過setText()函數設置到視圖上的文本輸出框中。

上面這個過程是視圖與控制器的對接,而控制器和計算模型的對接主要體如今Calculator類的核心函數core()中:將textIn內容傳遞給Readin實例化對象並調用其getWords方法,從而將文本內容處理爲單詞數組。隨後,根據用戶的參數設置,分-w和-c兩類,分別調用Core.dll的gen_chain_word和gen_chain_char函數(經過dll),從而 或得出計算結果(長度和單詞鏈)、或接收拋出的異常(無解或無-r卻成環)。最後,將計算結果或異常提示信息設置到控制器Calculator類的textOut變量中,再由控制器傳遞到視圖層在輸出文本框中。

實現的功能截圖以下:
(1)初始界面:

(2)一個正確的樣例:

(3)一個測試異常的樣例:

(十二)描述結對的過程,提供非擺拍的兩人在討論的結對照片。

整個項目的實現過程當中,因爲時間上很差安排、不大熟悉、性格比較內向等緣由,咱們大多數時候仍是並行工做,保持線上交流討論、互報進度,每隔1-3天線下開個小會,進行需求分析、工做分配、問題討論、模塊對接等工做。具體以下:

時間/事件 16061155王冰 16061093謝靜芬
第一次開會前 粗讀項目核心要求,開始編寫初版代碼 研讀和分析項目要求,記錄問題,考慮如何分工
第一次開會 討論需求中不明確的地方;而後一致認爲,因爲時間緊任務重、二人時空上不大方便進行結對編程(沒法像其餘組那樣串宿舍結對編程...)等緣由,決定二人仍是並行工做;建倉庫,進行分工
第一次開會後 編寫完成初版核心代碼,包括命令行參數處理、文本單詞提取、最長鏈計算 設計異常種類、構造測試用例、安裝和學習qt、代碼複審
第二次開會前 進行測試,修復一些bug,測試性能,嘗試優化性能 設計和編寫GUI,代碼複審,開始書寫博客的共同部分
第二次開會 將GUI和計算核心進行對接(暫未轉成dll),修復了一些bug。各自查閱並嘗試進行dll的生成和調用(失敗了)
第二次開會後 繼續嘗試進行dll的生成和調用、書寫博客的共同部分中關於計算核心和優化等內容 書寫完成博客的共同部分中的其它部分、找到一些不錯的連接便於隊友學習博客中提到的一些概念
第三次開會 將GUI和計算核心dll進行對接,成功!與另外一組同窗的模塊進行交換對接,成功!
第三次開會後 各自完成博客中的非共同部分
總結 真是太優秀了! 打雜也很認真!

討論時的照片:

(十三)看教科書和其它參考書,網站中關於結對編程的章節,例如這個,說明結對編程的優勢和缺點。結對的每個人的優勢和缺點在哪裏 (要列出至少三個優勢和一個缺點)。

結對編程的優勢:

  1. 每人在各自獨立設計、實現軟件的過程當中難免要犯這樣那樣的錯誤。在結對編程中,由於有隨時的複審和交流,程序各方面的質量取決於一對程序員中各方面水平較高的那一位。程序中的錯誤就會少得多,程序的初始質量會高不少。
  2. 程序的初始質量高了,就會省下不少之後修改、測試的時間。
  3. 在企業管理層次上,結對能更有效地交流,相互學習和傳遞經驗,能更好地處理人員流動。由於一我的的知識已經被其餘人共享。
  4. 輪流工做:讓程序員輪流工做,從而避免出現過分思考而致使觀察力和判斷力降低。

缺點:

  1. 結對的雙方若是脾氣對味,技術觀點相近,結對會很愉快,並且碰撞出不少火花,效率有明顯提升。反之,就可能陷入不少的爭吵,而致使進度停滯不前。甚至影響團隊協做。
  2. 結對編程所花費的時間較多,雖而後期修改和測試的時間可能會減小。
  3. 須要有一個領航員,且該領航員恰當地發揮做用,只有兩個副駕駛員是不能開飛機的。
  4. 有些人的性格上喜歡單兵做戰,不喜歡、不習慣,適應結對編程須要必定的時間

我認爲的,結對二人的優缺點:

優勢 缺點
16061155 王冰 1.代碼能力強,項目核心代碼的編寫者(十分感謝王冰隊友的carry!
2.交流溝通、獲取信息的能力較強,所以解決告終對過程當中的不少小問題。
3.十分負責任,合做真誠。
1.偶爾會粗心、考慮不嚴密
2.晚睡晚起
16061093 謝靜芬 1.善於作計劃、分配和管理。
2.邏輯清晰嚴密,較擅長測試和寫文檔(誤)。
3.蒐集資料、學習新東西的能力較強。
1.編程和算法能力較弱。
2.人際交流能力較弱。
3.脾氣容易暴躁。

(十四)在你實現完程序以後,在附錄提供的PSP表格記錄下你在程序的各個模塊上實際花費的時間。

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 20 20
· Estimate · 估計這個任務須要多少時間 20 20
Development 開發 840 1780
· Analysis · 需求分析 (包括學習新技術) 60 120
· Design Spec · 生成設計文檔 20 15
· Design Review · 設計複審 (和同事審覈設計文檔) 10 10
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 10 5
· Design · 具體設計 50 40
· Coding · 具體編碼 500 1500
· Code Review · 代碼複審 40 30
· Test · 測試(自我測試,修改代碼,提交修改) 150 60
Reporting 報告 60 105
· Test Report · 測試報告 30 40
· Size Measurement · 計算工做量 10 5
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 20 60
合計 920 1905

(十五)與其餘隊進行鬆耦合

咱們隊把咱們隊的Core.dll與其餘隊的Core.dll進行了交換,並使用咱們的GUI與他們的dll進行耦合,結果比較符合要求,成功運行。

最終,咱們與16061173鮑屹偉16061135張沛澤一組、16061167白世豪16061170宋卓洋一組以及16061144餘宸狄16061137張朝陽一組交換了程序,進行了鬆耦合。

在咱們的GUI上調用其餘組的dll:

16061173鮑屹偉16061135張沛澤調用咱們的dll:

16061167白世豪16061170宋卓洋調用咱們的dll:

16061144餘宸狄16061137張朝陽調用咱們的dll:

相關文章
相關標籤/搜索