【軟工】結對編程

【軟工】結對編程

本做業屬於課程軟件工程
做業要求點此c++

第一部分: github地址

源碼在githubgit

第二部分:PSP表(包括了實際完成的PSP表)

Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
計劃 30 30
· 估計這個任務須要多少時間 30 30
開發 910 1690
· 需求分析 (包括學習新技術) 300 180
· 生成設計文檔 30 60
· 設計複審 (和同事審覈設計文檔) 60 30
· 代碼規範 (爲目前的開發制定合適的規範) 30 60
· 具體設計 100 200
· 具體編碼 430 900
· 代碼複審 60 60
· 測試(自我測試,修改代碼,提交修改) 200 200
報告 290 260
· 測試報告 130 200
· 計算工做量 30 30
· 過後總結, 並提出過程改進計劃 30 30
合計 1430 1980

第三部分:接口設計方法

1.信息隱藏:類的全部屬性私有,只有須要被調用的方法共有
2.接口設計:參數處理、文件讀入、單詞鏈計算功能獨立,互不重疊
3.鬆耦合:核心能夠單獨調用,並經過了交換測試github

第四部分:計算模塊接口的設計與實現過程

算法設計

對於計算模塊,首先要作的就是將讀入的單詞列表構建爲一個圖,比較基本的兩種思路分別是算法

  • 以單詞做爲頂點、首尾字母做爲邊;
  • 以首尾字母做爲頂點、單詞做爲邊。

由於咱們須要分別計算最多單詞數和最多字母數的最長單詞鏈,採用第二種方法時,對於求最多字母數的最長單詞鏈,能夠直接將單詞的字母數做爲圖中邊的權重;對於計算最多單詞數的最長單詞鏈,能夠將各個邊的權重都初始化爲1。這樣就能夠將兩個函數後續的求解過程統一塊兒來,將求解最長單詞鏈的問題轉化爲求一個有向帶權圖的最長路徑問題,明顯要比第一種方法便捷,因此我首先們採用第二種方法來構建一個帶權有向圖。編程

對圖進行初始化之後,第二步須要判斷構造的圖是有環圖仍是無環圖,而後分別採起不一樣的算法。這裏咱們採用的判斷方法爲進行拓撲排序,若是全部頂點能構成一個拓撲序列,說明圖中無環,不然有換。less

對於有向無環圖(DAG)的最長路徑問題,能夠將權值取負,而後就轉化爲求一個不帶負權環的最短路徑問題,或者直接用Floyd等算法來求最長路徑,可是由於已經進行了拓撲排序,因此最簡便的方法仍是直接採用動態規劃方法按排序結果求解最長路徑。對於以鄰接表存儲的圖,拓撲排序的時間複雜度是O(V+E),求出拓撲順序後動態規劃算法的時間複雜度也爲O(V+E),所以算法總的時間複雜度爲O(V+E)。函數

對於帶環圖,求其最長路徑屬於NP-Hard問題,並無比較高效的算法。咱們採用的方法主要是按照以每一個頂點做爲起始點或根據所求的起始頂點,採用深度優先搜索找出全部的路徑,而後從中選擇最長的路徑。工具

算法的獨到之處主要在於經過在構建有向圖時,經過賦不一樣的權值將求解最多單詞和最多字母的問題統一塊兒來,便於後續的求解,並增長了代碼的複用性。使用的拓撲排序策略不只能夠判斷圖中是否有環,更爲無環圖的求解奠基了基礎。性能

計算模塊實現

計算模塊主要包括一個WordInfo類型和WordGraph,Compute兩個類,另外還有gen_chain_word,gen_chain_char兩個接口函數,類中的成員和函數的詳細信息見下面的類圖。單元測試

WordInfo類型記錄了單個單詞的各項信息,如起始字母、終止字母,權重,在單詞列表中的位置等。

WordGraph類將根據輸入的單詞列表構建一個有向加權圖,並對圖進行拓撲排序,判斷是否有環。

Compute類中含有一個WordGraph成員,根據圖的類型採用不一樣的算法進行最長單詞鏈的計算。

第五部分:UML圖顯示計算模塊部分各個實體

點擊菜單欄「工具」→「獲取工具和功能」 獲取組件類設計器,而後可用經過對應文件的右鍵菜單中的「查看類圖選項」直接生成類圖。

第六部分:計算模塊接口部分的性能改進

在改進計算模塊接口部分的性能上大概花費了2天的時間。

改進思路

計算模塊在求最長路徑時,對於有環圖和無環圖,首先採用的方法爲以字母爲頂點,單詞爲邊構造鄰接表,而後以每一個頂點做爲起始點採用深度優先搜索,找出全部的路徑,從中選擇符合要求的最長路徑的基本算法,確保首先可以實現基本的生成功能,避免出現「過早優化」。性能的改進主要在如下幾個方面:

  1. 採用拓撲排序對生成的圖進行排序,判斷圖中是否有環;
  2. 在根據輸入單詞初始化圖時,將首尾字母分別相等(但首字母與尾字母不一樣)的多個單詞合成一條邊,權值取單詞中長度最長的一個,簡化生成的圖,便於後續的拓撲排序(若是排序後發現爲有環圖,則說明兩個頂點之間的多條同向邊不能合併,在使用dfs搜索以前再從新初始化圖)。
  3. 對於無環圖,採用動態規劃算法依次求出以每一個頂點爲尾節點的最長路徑,沒必要搜索全部路徑;
  4. 對於指定首字母或尾字母的有環圖,只搜索全部以該字母開始或結束的路徑,而不是搜索全部路徑。
  5. 對於較爲複雜的有環圖,有想過用一些啓發式算法來求解,可是這類算法在不少時候求得的都是局部最優解,並不能保證必定能求得全局最優解,與咱們題目的要求不符,於是最終沒有采用這類算法。

性能分析

如下是讀入一個不帶單詞環文本時的性能分析圖,能夠看出除main函數外,花費最多的函數爲File_handle類中的deal_file函數,時間主要花費在文本讀入過程當中。

如下是讀入一個帶單詞環文本時的性能分析圖,能夠看出程序中消耗最大的函數爲Core模塊的dfs函數,主要時間都花費在了路徑搜索上,花費了99%以上的時間。

第七部分:契約式設計

優:
容許在編譯時檢查程序的正確性
極大的保證了程序正確性
缺:
在生產中沒法自由地把這些契約disable
對於大量輸入輸出的函數拖慢運行速度

融入:每一個模塊都設計了獨立的數據檢查

第八部分:計算模塊部分單元測試展現

部分單元測試代碼

對於計算模塊接口的測試主要分爲兩大類,正常狀況和異常狀況處理,本部分主要介紹正常輸入下的單元測試,異常處理將在下一部分介紹。

1.簡單測試

TEST_METHOD(TestMethod1)
        {
            char* input[4] = { "END", "OF", "THE", "WORLD" };
            char* result[4] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_word(input, 4, result, 0, 0, false);
            Assert::AreEqual(len, 2);
        }

2.最多單詞測試

TEST_METHOD(TestMethod2)
        {
            char* input[11] = { "Algebra", "Apple", "Zoo", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf", "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_word(input, 11, result, 0, 0, false);
            Assert::AreEqual(len, 4);
        }

3.最多字母測試

TEST_METHOD(TestMethod3)
        {
            char* input[11] = { "Algebra", "Apple", "Zoo", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf", "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_char(input, 11, result, 0, 0, false);
            Assert::AreEqual(len, 2);
        }

4.最多單詞帶環測試

TEST_METHOD(TestMethod6)
        {
            char* input[5] = { "Algebra", "Applea", "Zooa", "Elephant", "Under" };
            char* result[5] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_word(input, 5, result, 0, 0, true);
            Assert::AreEqual(len, 3);
        }

5.最多字母帶環測試

TEST_METHOD(TestMethod7)
        {
            char* input[5] = { "Algebra", "Applea", "Zooa", "Elephant", "Under" };
            char* result[5] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_char(input, 5, result, 0, 0, true);
            Assert::AreEqual(len, 3);
        }

6.最多單詞特殊測試

TEST_METHOD(TestMethod10)
        {
            char* input[5] = { "aa", "aaa", "aaaa", "aaaaa", "a" };
            char* result[5] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_word(input, 5, result, 0, 0, true);
            Assert::AreEqual(len, 5);
        }

7.最多字母特殊測試

TEST_METHOD(TestMethod11)
        {
            char* input[5] = { "aa", "aaa", "aaaa", "aaaaa", "a" };
            char* result[5] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_char(input, 5, result, 0, 0, true);
            Assert::AreEqual(len, 5);
        }

8.最多單詞指定首字母測試

TEST_METHOD(TestMethod12)
        {
            char* input[11] = { "Algebra", "Apple", "Zoo", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf", "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_word(input, 11, result, 'a', 0, false);
            Assert::AreEqual(len, 4);
        }

9.最多字母指定首字母測試

TEST_METHOD(TestMethod13)
        {
            char* input[11] = { "Algebra", "Apple", "Zoo", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf", "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            /* 調用Core中封裝好的函數 */
            int len = gen_chain_char(input, 11, result, 'a', 0, false);
            Assert::AreEqual(len, 4);
        }

此外還包括指定尾字母測試,指定首尾字母測試

測試覆蓋率截圖

第九部分:計算模塊部分異常處理說明

計算模塊部分的異常主要包括傳入的參數不合法(words指針、result指針爲空,參數len爲負數,首尾字母約束不合法)、單詞中包含環但沒有-r參數、輸入的單詞沒法構成單詞鏈等。

1.最多單詞測試,包含環但沒有-r參數

TEST_METHOD(TestMethod8)
        {
            char* input[5] = { "Algebra", "Applea", "Zooa", "Elephant", "Under" };
            char* result[5] = { 0 };
            /* 調用Core中封裝好的函數 */
            
            try {
                int len = gen_chain_char(input, 5, result, 0, 0, false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Error: word text implies word rings");
            }

        }

2.最多字母測試,包含環但沒有-r參數

TEST_METHOD(TestMethod9)
        {
            char* input[5] = { "Algebra", "Applea", "Zooa", "Elephant", "Under" };
            char* result[5] = { 0 };
            /* 調用Core中封裝好的函數 */
            try {
                int len = gen_chain_char(input, 5, result, 0, 0, false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Error: word text implies word rings");
            }
        }

3.最多單詞測試,無單詞鏈

TEST_METHOD(TestMethod4)
        {
            char* input[7] = { "Algebra", "Zoo", "Under", "Dog", "Moon", "Leaf",
                                    "Trick" };
            char* result[7] = { 0 };
            /* 調用Core中封裝好的函數 */
            try {
                int len = gen_chain_word(input, 7, result, 0, 0, false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Error: no word chain meets the requirements");
            }
        }

4.最多字母測試,無單詞鏈

TEST_METHOD(TestMethod5)
        {
            char* input[7] = { "Algebra", "Zoo", "Under", "Dog", "Moon", "Leaf",
                                    "Trick" };
            char* result[7] = { 0 };
            /* 調用Core中封裝好的函數 */
            try {
                int len = gen_chain_char(input, 7, result, 0, 0, false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Error: no word chain meets the requirements");
            }
        }

5.不存在指定首尾字母的單詞鏈測試

TEST_METHOD(TestMethod17)
        {
            char* input[11] = { "Algebra", "Apple", "Zoo", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf",
                                    "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            /* 調用Core中封裝好的函數 */
            try {
                int len = gen_chain_char(input, 11, result, 'a', 'm', false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Error: no word chain meets the requirements");
            }
        }

6.參數不合法:words指針爲空

TEST_METHOD(TestMethod22)
        {
            char* result[11] = { 0 }; 
            try {
                int len = gen_chain_word(NULL, 11, result, 0, 'm', false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Invalid parameter: 'words' or 'result' is null.");
            }
        }

7.參數不合法:len長度爲負

TEST_METHOD(TestMethod25)
        {
            char* input[11] = { "Algebra", "Apple", "Zop", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf",
                                    "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            try {
                int len = gen_chain_word(input, -1, result, 0, 'm', false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Invalid parameter: 'len' is less than 0.");
            }
        }

8.參數不合法:首尾字母約束不合法

TEST_METHOD(TestMethod28)
        {
            char* input[11] = { "Algebra", "Apple", "Zop", "Elephant", "Under", "Fox", "Dog", "Moon", "Leaf",
                                    "Trick", "Pseudopseudohypoparathyroidism" };
            char* result[11] = { 0 };
            try {
                int len = gen_chain_char(input, 11, result, 'a', '0', false);
            }
            catch (const char *error_message) {
                Assert::AreEqual(error_message, "Invlid parameter: 'head' or 'tail' is not a letter or 0.");
            }
        }

第十部分:界面模塊設計的詳細設計

設計

咱們的圖形用戶界面採用了github上開源的圖形界面庫imgui。圖形界面能夠分爲如下四部分:

  1. 輸入:包括經過Select File按鈕導入文本以及文本框輸入文本兩種輸入方式,導入文件後文件的內容會出如今輸入框中;
  2. 參數選擇:經過參數選擇框選擇對應的參數,選擇的參數發生缺乏、衝突 、錯誤時會實時給出提示信息;
  3. 運行:當選擇的參數無誤後會顯示Run按鈕,用戶點擊Run按鈕後調用核心模塊計算;
  4. 輸出:點擊Run按鈕計算完成以後運行結果以及Export按鈕顯示在按鈕下方,點擊Export按鈕將跳出路徑選擇窗口,用戶選擇路徑後將結果保存到用戶指定的位置。

實現

用imgui開發圖形用戶界面比較便捷,先從庫中選擇對應的模版,而後在main函數的對應位置設置組件便可,部分組件的實現過程以下:

//顯示提示信息
    ImGui::Begin("Word_chain");
    ImGui::Text("Please input words or select file:");
    //顯示文本選擇按鈕
    ImGui::Button("Select File")

    //參數選擇框
    ImGui::Text("Please select parameters:");  
    ImGui::Checkbox("-w", &par_w)

    ImGui::Checkbox("-h", &par_h)
    ImGui::InputText(label_head, head, 2, 0, NULL, NULL)    
    
    //產生計算結果後將結果顯示在屏幕上並實現按鈕導出功能
    if (par_right && show_result) {
                ImGui::Text("Result:\n%s", answer.c_str());
                if (ImGui::Button("Export")) {
                    if (get_save_file_name(save_file_name)) {
                        outf.open(save_file_name);
                        if (outf) {
                            outf << answer;
                        }
                        outf.close();
                    }
                }
            }

第十一部分:界面模塊與計算模塊對接

對接過程

當參數選對時纔會顯示Run按鈕,用戶點擊Run按鈕後會進行與計算模塊的對接,先對輸入框中的單詞進行分割,而後調用gen_chain_word、gen_chain_char函數進行計算

計算出結果後會將結果顯示在在屏幕上並顯示導出按鈕。

//用戶點擊Run按鈕後執行if語句中的內容調用計算模塊的接口進行計算
            if (par_right && ImGui::Button("Run")) {
                char h, t;
                answer.clear();
                get_words(buf, len, words);
                h = par_h ? head[0] : 0;
                t = par_t ? tail[0] : 0;
                int cnt = 0;
                if (par_w) {
                    cnt = gen_chain_word(pwords, wordnum, result, h, t, par_r);
                }
                else if (par_c) {
                    cnt = gen_chain_char(pwords, wordnum, result, head[0], tail[0], par_r);
                }

                for (int i = 0; i < cnt; i++) {
                    answer += result[i];
                    answer += "\n";
                }
                show_result = true;
            }

           //產生計算結果後將結果顯示在屏幕上並實現按鈕導出功能
            if (par_right && show_result) {
                ImGui::Text("Result:\n%s", answer.c_str());
                if (ImGui::Button("Export")) {
                    if (get_save_file_name(save_file_name)) {
                        outf.open(save_file_name);
                        if (outf) {
                            outf << answer;
                        }
                        outf.close();
                    }
                }
            }

圖形界面截圖

第十二部分:結對過程

咱們的結對編程主要按照領航員與駕駛員的模式,兩人輪流編程,不斷交替,一人編程時另外一人負責監督和檢查。

第十三部份:結對編程優缺點

優勢:
一、互相鼓勵,不容易沮喪:團隊工做能增長成員的工做積極性。由於在面對問題的時候,會有人一塊兒分擔,共同嘗試新的策略。
二、互相監督,不容易偷懶:兩我的一塊兒工做須要互相配合,若是想偷懶去幹別的,就會拖延工做進度。
三、互相學習編程技巧:在編程中,相互討論,能夠更快更有效地解決問題,互相請教對方,能夠獲得能力上的互補。
四、能夠培養和訓練新人:讓資深開發者和新手一塊兒工做,可讓新人更快上手。
五、每時每刻都處在代碼複審的狀態,便於發現問題:兩人互相監督工做,能夠加強代碼和產品質量,並有效的減小BUG。
缺點:
一、與合不來的人一塊兒編程容易發生爭執,不利於團隊和諧。
二、經驗豐富的老手可能會對新手產生不滿的情緒。
三、一山不容二虎,開發者之間可能就某一問題發生分歧,產生矛盾,形成沒必要要的內耗。
四、開發人員可能會在工做時交談一些與工做無關的事,分散注意力,形成效率低下。
五、場地不方便,難於約到共同空閒的時間

我我的的優勢:
1.起步早,天天寫一點。
2.積極交流。
3.臉皮較厚,強湊了三條優勢。

我的缺點:
1.粗心,懶。
2.代碼能力差。

個人結對對象優勢:
1.代碼能力強。
2.熬得住深夜,看得見晨光。
3.求知慾強。
4.幫助同窗,有求必應。

缺點:
1.沉默寡言。

第十四部分:PSP表

合併至第二部分。

附加題

1.GUI部分見博客第9、第十部分。

使用說明(界面見第十部分):
1.可使用select file按鈕選擇文件,也能夠直接在input words文本框輸入。
2.提供了參數選擇框,參數錯誤時會給予提示;僅當參數正確時纔會彈出run按鈕。
3.run的結果直接顯示在GUI上,並可使用Export按鈕導出。

2.鬆耦合測試:(16061175 16061156)GUI AND(16061170 16061167) Core

耦合測試直接可調用,可是運行時發現了不一樣的運行結果;經查證交換的Core部分算法有問題並告知合做小組同窗修正了。目前雙方執行結果一致。

相關文章
相關標籤/搜索