結對項目-最長單詞鏈

項目 內容
這個做業屬於哪一個課程 https://edu.cnblogs.com/campus/buaa/BUAA_SE_2019_LJ
這個做業的要求在哪裏 https://edu.cnblogs.com/campus/buaa/BUAA_SE_2019_LJ/homework/2638
我在這個課程的目標是 學會軟件工程,爲之後工做打好基礎。
這個做業在哪一個具體方面幫助我實現目標 實踐告終對編程,體會了軟件工程的作法

1.Github項目地址:

github地址html

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

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 20 20
· Estimate · 估計這個任務須要多少時間 20 20
Development 開發 840 760
· Analysis · 需求分析 (包括學習新技術) 60 120
· Design Spec · 生成設計文檔 20 15
· Design Review · 設計複審 (和同事審覈設計文檔) 10 10
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 10 5
· Design · 具體設計 50 40
· Coding · 具體編碼 500 480
· 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

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

  1. Information Hiding信息隱藏
    這是指模塊內不須要暴露的信息要隱藏起來,也就是作好模塊的封裝工做。做爲計算模塊的主要負責人,一個很重要的工做就是模塊的封裝、信息的隱藏,可能避免重要信息外泄引起不可預測的異常等,保證程序安全性。
  2. Interface Design接口設計
    接口是用於交互的,因此在設計接口時,主要須要考慮的是,「咱們會接受哪些信息」和「咱們須要反饋哪些信息」,相似於函數裏面接受什麼參數、返回什麼信息。接口設計對軟件工程團隊合做十分重要,同時應該遵循OO課講過的接口設計六大原則
  3. Loose Coupling鬆耦合
    IT界有一句很著名的口號——強內聚、鬆耦合:git

    咱們的程序要模塊化,模塊要完成明確的一組關聯的服務功能,要求它的各部分是相關的、有機組合起來是完總體(外部程序來看黑盒子),模塊的內部各成分之間相關聯程度要儘量高(強內聚);而模塊與模塊之間又要求是可分拆的、少依賴的(鬆耦合)。程序員

本次結對項目中,在構造類時,比較注意類的變量和函數的封裝,從而保證程序的安全性。其次,在分工前約定好了接口,以便於後續的代碼整合。在鬆耦合方面,咱們在設計藉口而的時候就考慮到這一點,設計模塊功能和接口的時候儘可能讓模塊之間的耦合更鬆,同時在進行GUI部分的時候,也用到了Model View Controller這個軟件設計典範,將計算模型、視圖界面、控制器分離進行代碼,這三個大模塊也實現了「鬆耦合」。github

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

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

Input類有兩個函數編程

函數名 函數做用 調用關係
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類中有兩個函數:ide

函數名 函數做用 調用關係
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的點,那麼說明成環。

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

5.閱讀有關UML的內容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。畫出UML圖顯示計算模塊部分各個實體之間的關係(畫一個圖便可)。

​ 類圖畫法:使用VS2017,右鍵點擊項目,找到查看,右邊會有類圖,以後點擊圖,就看到各個類的屬性關係,保存類圖,本身補充調用關係便可。

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

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

​ 消耗最大的函數:

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

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

​ 消耗最大的函數:

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

7.看Design by Contract, Code Contract的內容:
http://en.wikipedia.org/wiki/Design_by_contract
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述這些作法的優缺點, 說明你是如何把它們融入結對做業中的

參考這篇博客
契約式編程(Design by Contract)指的是調用者和被調用者地位平等,制定代碼契約(Code Contract),雙方必須彼此履行義務,才能夠行駛權利。簡單來講,就是調用者必須提供正確的參數(調用者的義務、被調用者的權利),被調用者必須保證正確的結果(調用者的權利、被調用者的義務)。

在契約式編程提出以前,軟件工程的主要理論是C/S模式。對於C/S模式,咱們看待兩個模塊的地位是不平等的,咱們每每要求server很是強大,能夠處理一切可能的異常,而對client漠不關心,形成了client代碼的低劣。這裏的client能夠理解爲調用者,server理解爲被調用者。

由此咱們能夠得出契約式編程的優勢:調用雙方都有必須履行的義務,也有使用的權利,這樣就保證了雙方代碼的質量,提升了軟件工程的效率和質量。

契約式編程的缺點在於,契約式編程須要一種機制來驗證契約的成立與否。而斷言顯然是最好的選擇,可是並非全部的程序語言都有斷言機制。那麼強行使用語言進行模仿就勢必形成代碼的冗餘和不可讀性的提升。

爲了實現上述契約式編程,咱們的作法是,在一些函數的功能性的代碼前加入了斷言assert,來驗證調用者是否遵循了一些最基本的契約,好比 在處理文件內容的函數Readin(filename)中,

在本項目中,咱們在代碼中設置了一些斷言機制,代表某個函數對於傳入的參數的一些最基本的要求,好比 處理文件內容的函數void GetWords(char * filename)內部會首先斷言傳入的filename非NULL。

8.計算模塊部分單元測試展現。 展現出項目部分單元測試代碼,並說明測試的函數,構造測試數據的思路。並將單元測試獲得的測試覆蓋率截圖,發表在博客中。要求整體覆蓋率到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%以上:

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

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

    (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!
-t 或-h後緊跟着的參數是單字符,但不是小寫字母 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

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

界面模塊使用VS+Qt,這裏分享兩個安裝教程:教程1教程2
整個界面是一個QDialog(因爲是一個小軟件因此沒有選擇QMainWindow)界面模塊主要包括「視圖view」和「控制controller」兩部分:
(一)經過所見既所得的Qt Designer設計UI視圖,對佈局作了一些考慮,放一張佈局設計圖:
//插入圖片

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

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

(二)經過信號與槽機制設置「哪一個組件的什麼事件會觸發哪一個類的什麼方法」。按照上述每一個組件的功能設置槽函數,並在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);
    }
}

11.界面模塊與計算模塊的對接。 詳細地描述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)一個測試異常的樣例:

12.描述結對的過程,提供非擺拍的兩人在討論的結對照片。

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

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

13.看教科書和其它參考書,網站中關於結對編程的章節,例如:
http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html
說明結對編程的優勢和缺點。
結對的每個人的優勢和缺點在哪裏 (要列出至少三個優勢和一個缺點)。

結對編程的優勢:

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

缺點:

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

本身優勢:

​ 1.寫代碼刻苦

​ 2.準備的比較多

​ 3.嘗試的比較多

本身缺點:

​ 有時候寫代碼思考的比較少,有不少沒考慮到的bug

隊友優勢:

​ 1.閱讀需求詳細

​ 2.思考覆蓋徹底

​ 3.代碼bug少

對優缺點:

​ 找不到缺點

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

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 20 20
· Estimate · 估計這個任務須要多少時間 20 20
Development 開發 840 760
· Analysis · 需求分析 (包括學習新技術) 60 120
· Design Spec · 生成設計文檔 20 15
· Design Review · 設計複審 (和同事審覈設計文檔) 10 10
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 10 5
· Design · 具體設計 50 40
· Coding · 具體編碼 500 480
· 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

15.與其餘隊進行鬆耦合。

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

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

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

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

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

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

相關文章
相關標籤/搜索