目錄html
PSP2.1 | Personal Software Process Stages | 預估耗時(小時) | 實際耗時(小時) |
Planning | 計劃 | 0.5 | |
· Estimate | · 估計這個任務須要多少時間 | 20.4 | |
Development | 開發 | 18.6 | |
· Analysis | · 需求分析 (包括學習新技術) | 5 | |
· Design Spec | · 生成設計文檔 | 0.5 | |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 0.5 | |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 0.1 | |
· Design | · 具體設計 | 0.5 | |
· Coding | · 具體編碼 | 10 | |
· Code Review | · 代碼複審 | 1 | |
· Test | · 測試(自我測試,修改代碼,提交修改) | 1 | |
Reporting | 報告 | 1 | |
· Test Report | · 測試報告 | 0.2 | |
· Size Measurement | · 計算工做量 | 0.1 | |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 0.5 | |
合計 | 21.9 |
計算模塊主要有6個類,Core類
,DFS類
,Error類
,Input類
,PreProcess類
,RingDfs類
。ios
Input類
用於從輸入中提取所須要的信息,如模式、開頭結尾、路徑等。git
PreProcess類
用於處理數據,包括分割單詞,並依據分割出的單詞生成圖,輸出結果等。github
DFS類
用於尋找圖中是否有環。算法
RingDfs類
用於根據生成的圖進行搜索,尋找符合要求的最長路徑。編程
Core類
用於封裝。後端
Error類
用於異常時報錯。安全
方法名 | 做用 | 調用方法 |
---|---|---|
Input(int num, char * paras[]) | 根據程序參數,提取相應信息,如各種參數,文件路徑等 | 本類私有方法,Error(string str)方法 |
Input(string str); | 根據輸入字符串提取相應信息 | 本類私有方法 |
int getMode() | 返回指定的單詞鏈類型,1爲單詞數,2爲字母數 | |
int getIfRing() | 返回是否容許環 | |
int getHead() | 返回指定的單詞鏈開頭,0爲無限制,不然爲字母的ASCII碼值 | |
int getTail() | 返回指定單詞鏈結尾,規則同上 | |
string getPath() | 返回輸入的文件路徑 | |
string getInput() | 返回輸入的完整字符串,用於調試 |
方法名 | 做用 | 調用方法 |
---|---|---|
PreProcess(char* wordss[], int len, int r); | 根據輸入的單詞表、長度、是否有環,判斷是否合法並生成圖 | 本地私有方法,Error(string str)方法 |
PreProcess(string str, int n, char* words[], int r) | 根據輸入的包含全部單詞的字符串、長度、是否有環,生成單詞表與圖。單詞表char* []是爲了知足接口要求。 | 本地私有方法 |
PreProcess(string str, int r) | 根據輸入的路徑與是否有環,生成單詞表與圖 | 本地私有方法 |
void printGraph() | 打印合並同首尾單詞後的圖 | |
void printRingGraph() | 答應元素 | |
void print(vector
|
根據輸入的容器,將其中單詞按順序輸出至solution.txt | |
void VecToStr(char* result[], vector
|
將圖轉化爲單詞表格式,以知足同接口要求 | Error(string str)方法 |
方法名 | 做用 | 調用方法 |
---|---|---|
DFS(PreProcess pp) | 生成實例 | |
void getGraph() | 獲取已生成好的圖 | |
bool hasRing() | 判斷圖中是否有除了自環之外的環 |
方法名 | 做用 | 調用方法 |
---|---|---|
RingDFS(PreProcess pp) | 生成實例,pp中含有圖等信息 | |
vector
|
根據輸入的各種參數,以及獲取到的圖,搜索符合要求的單詞鏈,並返回結果 | 本地私有方法,Error(string str)方法 |
方法名 | 做用 | 調用方法 |
---|---|---|
int gen_chain_word(char* words[], int len, char* result[], char h ead, char tail, bool enable_loop) | 根據輸入的單詞表、長度、參數等,搜索單詞數量最多的符合要求的單詞鏈 | Input::Input |
int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop) | 根據輸入的單詞表、長度、參數等,搜索字母數量最多的符合要求的單詞鏈 | 同上 |
方法名 | 做用 | 調用方法 |
---|---|---|
void Error(std::string str) | 根據輸入的報錯信息拋出異常 |
算法基於bfs的改進(改進原理見第6部分),基本原理簡單易懂。對於無環的狀況下,建圖時合併了同首尾單詞,只保留字母數量最大的單詞。數據結構
如下爲無環,單詞數量9900的運行狀況:
可見,大部分時間花在了建圖時的各類關於內存的操做上。這是由於在優化了無環的搜索算法後,將搜索部分的複雜度限制在了O(26x26),速度很快,所以關於內存的各種操做成爲了最佔用時間的操做。
對於算法的具體優化以下:
對於無環的搜索,按以下方法建圖:由於無環,因此以26個字母爲節點,單詞爲邊,以a
開頭b
結尾的單詞,表現爲由節點a
指向節點b
的一條邊。
在搜索算法上,以遞歸BFS搜索
爲基礎,在節點b
的遞歸退出時,便可確保b
日後的全部路徑均已遍歷,所以能夠獲得以b
爲開頭,能搜索到的最長符合要求的單詞鏈長度,保存該長度,記爲L(b)
。以後若其餘節點訪問節點b
時,好比上述例子中的a
節點訪問b
節點,不須要再次進入b
遞歸,只須要比較(L(b)
)與(由a
指向b
的邊長度)之和,與本來的L(a)
的大小,並將大者保存爲新的L(a)
便可。
這樣,在全部節點遞歸結束後,獲得了每一個節點日後所能達到的符合要求的單詞鏈的最大長度。根據此信息輸出最長路徑便可。
該算法確保了每條邊只走一次,不會重複走同一條邊(同一個單詞),所以,在無環的基礎上其複雜度爲O(26x26)。
可是,在有環的問題中,由於環的存在以及該方法沒法判斷L(b)
中有哪些節點,所以可能會致使重複走同一條邊,該算法沒法使用。
契約式設計的優勢是,義務、責任分工明確,各自負責各自部分的正確性,簡化了開發流程,明確了各自任務。契約的存在保障了雙方的利益,對需求者來講,不須要擔憂所需部分的正確性,只須要按照契約使用便可。對於供應者來講,不須要擔憂提供的模塊如何使用等, 只須要按照契約完成模塊並確保正確性便可。
在結對編程中,咱們會規定不一樣模塊之間的接口,在發現bug後將bug交給相應模塊的實現者負責修復,另外一方只在實現者遇到困難時提供幫助。
部分單元測試代碼以下:
這兩部分測試數據,測試的是方法Input Input(string str)
,方法Input Input(int num, char* paras[])
。兩個方法的做用分別是根據輸入的字符串,或程序的參數提取相應的設置信息,文件路徑等。
測試的思路是構造符合被測方法輸入數據結構要求的數據,並儘可能複雜,以達到更高的分支覆蓋率。
以圖中的測試用例爲例,由於測試的是處理輸入的方法,所以在構造輸入數據時儘可能構造較爲複雜的輸入,即儘可能輸入更多的參數,以覆蓋較多的分支。
對於異常測試,測試代碼以下:
在catch
方法中經過判斷異常信息是否符合預期的方式,來判斷是否觸發了所要測試的異常。
同時,在應該觸發異常的代碼下一行加入一條永假的斷言Assert::AreEqual("Error Test Didn't Throw Error!", " ");
,以確保經過的測試,是觸發了正確的異常,而不是沒有觸發異常致使沒有進入catch
,根本沒有運行catch
中的斷言。
測試的覆蓋率以下:
由於VS只有企業版、專業版才能查看單元測試的分支覆蓋率,因此該分支覆蓋率是將大部分測試中的代碼轉移到main
函數中運行後查看的,其覆蓋率低於實際的單元測試分支覆蓋率但也達到了90%。
全部的異常以下:
輸入錯誤
Chain Type Parameter Error
在已經輸入過-w
或-c
參數,即指定過單詞鏈類型的狀況下,再次輸入這兩個參數。
單元測試:
try { Input input("-w -c ..\Pair_Programming\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Chain Type Parameter Error"; Assert::AreEqual(s, str); }
Head Duplicate Definition Error
在已經使用-h
指令規定過開頭的狀況下,再次指定開頭。
單元測試:
try { Input input("-w -h a -h a ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Head Duplicate Definition Error"; Assert::AreEqual(s, str); }
Lack Space Error
在一次性輸入全部參數時,參數之間、-h
指令與制定的開頭字母之間應有空格。沒有空格時拋出次異常。
單元測試:
try { Input input("-w -ha ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Lack Space Error"; Assert::AreEqual(s, str); }
Head Letter Error
指定的開頭字符不是字母。
單元測試:
try { Input input("-w -h + ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Head Letter Error"; Assert::AreEqual(s, str); }
Tail Duplicate Definition Error
使用-t
指令指定過結尾的狀況下再次使用該指令。
單元測試:
try { Input input("-w -t a -t a ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Tail Duplicate Definition Error"; Assert::AreEqual(s, str); }
Tail Letter Error
制定的結尾字符不是字母。
單元測試:
try { Input input("-w -t + ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Tail Letter Error"; Assert::AreEqual(s, str); }
Ring Parameter Duplicate
屢次使用-r
參數。
單元測試:
try { Input input("-w -r -r ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Ring Parameter Duplicate"; Assert::AreEqual(s, str); }
Parameter Type Error
輸入的參數不符合規定。
單元測試:
try { Input input("-w -z ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Parameter Type Error"; Assert::AreEqual(s, str); }
Lack Chain Type Parameter
在輸入中沒有使用-w
或-c
制定指定單詞鏈類型。
單元測試:
try { Input input("-h a ..\\Pair_Programming\\DFSTest2.txt"); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Lack Chain Type Parameter"; Assert::AreEqual(s, str); }
數據錯誤
Input File Doesn't Exit
輸入的文件路徑不存在。
單元測試:
try { PreProcess pp ("noFile.txt", 0); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Input File Doesn't Exit"; Assert::AreEqual(s, str); }
Multiple Self Ring Found
在不容許有環的狀況下,出現了多個自環。
單元測試:
try { PreProcess pp("..\\Pair_Programming\\PPErrorTest1.txt", 0); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Multiple Self Ring Found"; Assert::AreEqual(s, str); }
其中,PPPErrorTest1.txt
的內容以下:
bc cc cd df ff beee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeee ef fg gb
No Word Chain in File
輸入的文件中單次數量小於2,不構成單詞鏈。
單元測試:
try { PreProcess pp("..\\Pair_Programming\\PPErrorTest2.txt", 0); } catch (string str) { string s = "No Word Chain in File"; Assert::AreEqual(s, str); }
其中,PPErrortEST2.TXT
文件內容以下:
bc
Ring Detected When not Allowed
在沒有使用-r
參數,的狀況下,單詞鏈中出現了環。
單元測試:
try { PreProcess pp("..\\Pair_Programming\\DFSTest1.txt", 0); DFS dfs(pp); dfs.getGraph(); dfs.hasRing(0); Assert::AreEqual("Error Test Didn't Throw Error!", " "); } catch (string str) { string s = "Ring Detected When not Allowed"; Assert::AreEqual(s, str); }
其中,DFSTest1.txt
文件的內容以下:
bc cc cd df ff beee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ef fg gb
使用的是Qt Creator 3.4.2進行開發的,因爲Qt自帶了前端設計的功能,開發起來比較簡單。
使用的是Qt Creator自帶的設計面板,操做簡單隻須要拖拽便可,如下是具體設計。
在設計好了界面以後,只須要編寫好slots函數就可在設計面板中簡單的關聯(代碼未所有貼出)
mainwindow.h:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; bool H; int wc; bool R; bool T; QString tmp; bool fFile; private slots: void setC(bool b); }; #endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <iostream> #include "qtextbrowser.h" #include <string> #include <QFile> #include <QDir> #include <QTextStream> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //ui->hideButton->hide(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::setC(bool b){ this->wc = b?2:1; }
編寫完成後,須要作的就是將函數與組件關聯起來,一樣在設計面板中操做。
關於Qt的信號槽機制,具體能夠看這篇博客,因爲是在設計模板中關聯的,因此並不須要手動編寫connect函數。
首先,將計算模塊打包成dll
文件,爲了使用方便,將統一的接口gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
與接口gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
定義爲函數而非類方法。
打包好後,將生成的Core.dll
文件複製到工程的debug
文件夾或release
文件夾中,經過如下代碼導入兩個上述接口:
typedef int(*ptr_word)(char* words[], int len, char* result[], char head, char tail, bool enable_loop); typedef int(*ptr_char)(char* words[], int len, char* result[], char head, char tail, bool enable_loop); HINSTANCE CoreDLL = LoadLibrary("Core.dll"); ptr_word gen_chain_word = (ptr_word)GetProcAddress(CoreDLL, "gen_chain_word"); ptr_char gen_chain_char = (ptr_word)GetProcAddress(CoreDLL, "gen_chain_char");
導入後,即可以直接調用兩個函數。
在結對的過程當中,首先咱們在一塊兒預估各個難度的複雜度,以後對於部分邏輯極爲簡單且不重要的部分如讀入等,進行分工,並行開發提升速度。對於核心的部分,如搜索算法的完成、優化等,咱們按照結對編程的原則,一人寫一人檢查,確保沒有BUG以及算法的正確性。
正如《構建之法》中所述
既然代碼複審能發現這麼多問題,有這麼好的效 果,若是咱們每時每刻都處在代碼複審的狀態, 那不是很好麼?
結對編程是將代碼複審發揮到極致後的形式,也就是說結對編程徹底繼承了代碼複審的全部優勢:加強了代碼的規範性,減小了bug出現的可能性,提升了代碼的可維護性等等。在合理的使用方法下,結對編程的效率甚至能超過並行開發的效率,考慮到測試、維護等等問題的時間成本。可是,這樣的「極限」形式也有必定的侷限,即對於程序中很是簡單,邏輯複雜度極地的部分,進行實時的代碼複審就有了「殺雞用牛刀」的意味,在這些部分上,結對編程顯然有矯枉過正的嫌疑,其開發效率在程序的簡單部分中顯然沒有並行開發高。
結對中個人優缺點:
優勢:承擔任務主動,投入時間長、任務完成的較爲完整,進行了許多優化。
缺點:與同伴溝通不足。
個人同伴的優缺點:
優勢:配合良好,完成任務優秀、負責,測試完整。
缺點:沒有及時反饋進度與問題。
PSP2.1 | Personal Software Process Stages | 預估耗時(小時) | 實際耗時(小時) |
Planning | 計劃 | 0.5 | 0.5 |
· Estimate | · 估計這個任務須要多少時間 | 20.4 | 55+ |
Development | 開發 | 18.6 | 50+ |
· Analysis | · 需求分析 (包括學習新技術) | 5 | 1 |
· Design Spec | · 生成設計文檔 | 0.5 | 0.2 |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 0.5 | 0.1 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 0.1 | 0.1 |
· Design | · 具體設計 | 0.5 | 1 |
· Coding | · 具體編碼 | 10 | 40+ |
· Code Review | · 代碼複審 | 1 | 1 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 1 | 10+ |
Reporting | 報告 | 1 | 2 |
· Test Report | · 測試報告 | 0.2 | 0.5 |
· Size Measurement | · 計算工做量 | 0.1 | 0.1 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 0.5 | 0.5 |
合計 | 21.9 | 55+ |
咱們與學號1606109三、16061155的隊伍進行了鬆耦合測試,結果正確,對方使用我方Core.dll
的運行結果截圖以下:
在剛開始耦合時出現了問題,由於在對於head
的定義中,咱們組的邏輯是當head
不爲0即認爲是規定的開頭字母,但接口內部沒有檢查其正確性,而對方認爲head
不規定時爲'0'
。
出現問題後,咱們組在接口內部加2上了對於參數的合法性判斷,以後沒有其餘問題。
以後咱們還與1606116七、16061170組進行了耦合,運行正確,沒有問題。