https://github.com/swearitagain/wordlistnode
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
Planning | 計劃 | 10 | 20 |
· Estimate | · 估計這個任務須要多少時間 | 10 | 20 |
Development | 開發 | 870 | 1470 |
· Analysis | · 需求分析 (包括學習新技術) | 60 | 120 |
· Design Spec | · 生成設計文檔 | 40 | 40 |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 30 | 30 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 20 | 20 |
· Design | · 具體設計 | 60 | 100 |
· Coding | · 具體編碼 | 400 | 1000 |
· Code Review | · 代碼複審 | 60 | 40 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 120 | 120 |
Reporting | 報告 | 70 | 120 |
· Test Report | · 測試報告 | 20 | 30 |
· Size Measurement | · 計算工做量 | 20 | 30 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 30 | 60 |
合計 | 950 | 1610 |
Information Hiding,Interface Design, Loose Couplinggit
首先參考wikipedia定義:程序員
information hiding is the principle of segregation of the design decisions in a computer program that are most likely to change, thus protecting other parts of the program from extensive modification if the design decision is changed
就是把數據封裝起來,防止變化的部分對於原有數據的破壞,在結對編程中就是面向對象的實現,好比把輸入輸出封裝爲input_output類,將讀入的數據暴露一個input函數,對外返回的是vector<string>。github
接口按照https://edu.cnblogs.com/campus/buaa/BUAA_SE_2019_LJ/homework/2638 設計編程
static int gen_chain(char* words[], int len, char* result[]); static int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop); static int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
鬆耦合是指在編程的時候讓一個部分儘量少地依賴其餘部分的組件,這樣就算由於需求更改而重寫之前的函數,也能避免對其餘沒有變化的部分形成影響。數組
鬆耦合在本次結對編程項目中主要體如今對於函數以及.cpp的封裝上,保證每一個模塊的功能獨立性,好比input_output.cpp只是對於輸入輸出的處理,calculate只是對於計算的處理。app
首先定義了一個接口類做爲基類,暴露出調用者須要的方法,其中get_result()函數返回計算模塊計算的結果:ide
class calculateInterface { public: virtual ~calculateInterface(); virtual vector<string> *get_result()=0; };
其次設計了calculate子類來完成最基本的計算功能: 這個類實現了接口類中定義的get_result方法,並新增了一個構造方法,以及一些私有的成員和方法。構造方法傳入單詞文本,以及-c參數。 私有方法中chain_find_next方法做用爲找到當前單詞結點的全部可以成鏈的下一個單詞,而check_current_chain判斷當前鏈是不是找到的最大鏈。
class calculate : public calculateInterface { public: calculate(vector<string> words, bool more_letter); ~calculate(); vector<string> *get_result() override; protected: vector<word_node> word_map[ALPHA_COUNT]; bool has_circle = false; bool more_letter; int longest_letter_count = 0; int current_letter_count = 0; vector<string> longest_word_chain; vector<string> current_word_chain; virtual bool chain_find_next(word_node prev_node); virtual void check_current_chain(); };
再而後設計了specified_calculate類,該類繼承calculate類,支持了指定鏈首尾字母的功能。 該類重寫了calculate類的get_result方法和check_current_chain方法,保留使用了父類的chain_find_next方法。
class specified_calculate : public calculate { public: //構造函數四個參數: //1. 字符串數組,由全部單詞構成 //2. 布爾變量,是否按照字母最多計算單詞鏈 //3. 整型,指定首字母,-1爲不指定,0-26對應26個字母 //4. 整型,指定尾字母,-1爲不指定,0-26對應26個字母 specified_calculate(vector<string> words, bool more_letter, int assigned_initail, int assigned_tail); ~specified_calculate(); vector<string> *get_result() override; void check_current_chain() override; protected: int assigned_initial; int assigned_tail; };
最後設計了circle_calculate類,該類繼承specified_calculate類,支持了容許單詞文本中隱含單詞環功能。 該類重寫了specified_calculate類的check_current_chain方法,保留使用了父類的其餘全部方法。
class circle_calculate : public specified_calculate { public: //構造函數五個參數: //1. 字符串數組,由全部單詞構成 //2. 布爾變量,是否按照字母最多計算單詞鏈 //3. 整型,指定首字母,-1爲不指定,0-26對應26個字母 //4. 整型,指定尾字母,-1爲不指定,0-26對應26個字母 //5. 布爾類型,是否容許文本隱含單詞環 circle_calculate(vector<string> words, bool more_letter, int assigned_initail, int assigned_tail, int circle); ~circle_calculate(); bool chain_find_next(word_node prev_node) override; protected: bool circle; };
類之間的關係能夠參考下文的UML圖。
咱們對項目進行了性能分析,發現項目的性能瓶頸在於遞歸迭代的深度過大。所以咱們優化了遞歸函數的結構,減小了其沒必要要的操做,性能稍有提高。
函數
最開始不是很瞭解Design By Contract的概念,在Wikipedia上獲得的標準定義以下:oop
//Design by contract Design by contract (DbC), also known as contract programming, programming by contract and design-by-contract programming, is an approach for designing software. It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants. //code contract The contracts take the form of pre-conditions, post-conditions, and object invariants. Contracts act as checked documentation of your external and internal APIs. The contracts are used to improve testing via runtime checking, enable static contract verification, and documentation generation.
Design By Contract也就是契約式設計,Code Contract規定了接口的數據類型,接口執行以前的條件(precondition)和接口執行以後的條件(postcondition)。
我認爲這種編程方式的優勢:
缺點:
項目中如何使用:
因爲在單元測試中不能使用命令行輸入,因此只須要設計函數的輸入,鑑定所須要的輸出便可,測試案例以下:
TEST_METHOD(test_gen_chain_w) { char *result[4]; char *words[4] = { "END", "OF", "THE", "WORLD" }; Assert::AreEqual(2, gen_chain(words, 4, result)); }
測試覆蓋率時使用OpenCppCoverage-0.9.6.1 VS插件進行,將單元測試模塊遷移到main函數中測試以後,測試覆蓋率圖以下:
異常類型 | 設計目標 |
---|---|
對於傳入的單詞文本爲空時的報錯 | |
單詞文本隱含單詞環 | 對於沒有-r參數時出現隱含單詞環的報錯 |
首尾字母約束不合法 | 對於單詞首/尾字母指定不合法的報錯 |
文本中某個單詞爲空 | 對於某個單詞爲空時的報錯 |
文本中某單詞首爲非字母 | 對於首字母爲非字母狀況的報錯 |
文本中某單詞尾爲非字母 | 對於尾字母爲非字母狀況的報錯 |
關於命令行輸入的異常以下:
拋出異常 | 說明 |
---|---|
-w param repeat | w參數重複 |
-c param repeat | c參數重複 |
-h param repeat | h參數重複 |
-t param repeat | t參數重複 |
-r param repeat | r參數重複 |
invalid param | 無效參數 |
非法輸入:文件不存在 | 非法輸入:文件不存在 |
本次只實現了命令行模塊。
在結對編程項目中構建了一個input_output類,專門處理從文本的輸入和將結果輸出到文本。
首先是從命令行的輸入,核心模塊是處理命令行的輸入:
while (i < in.size()) { if (in.at(i) == '-') { i++; //get next char char cur = in.at(i); if (cur == 'w') { if (is_w) { throw exception("-w param repeat"); } is_w = true; } else if (cur == 'c') { if (is_c) { throw exception("-c param repeat"); } is_c = true; } else if (cur == 'h') { if (is_h != 0) { throw exception("-h param repeat"); } i+=2; //get the blank char is_h = in.at(i); } else if (cur == 't') { if (is_t != 0) { throw exception("-t param repeat"); } i += 2; //get the blank char is_t = in.at(i); } else if (cur == 'r') { if (is_r) { throw exception("-r param repeat"); } is_r = true; } else { throw exception("invalid param"); } } else if (in.at(i) != ' ') { //read the absolute path of input file break; } i++; }
其中對於不符合規定的部分使用異常拋出,在main函數中接受異常。
本次只實現了命令行模塊。
根據解耦合的思想,設計了一個專門的input_output
class input_output { public: input_output(); ~input_output(); vector<string> input(); void output(vector<string> words); vector<string> words; bool is_w; //word-按單詞數量統計 bool is_c; //count-按字母數量統計 char is_h; //head-指定首字母 char is_t; //tail-指定尾字母 bool is_r; //round-是否成環 string in_path; //輸入文件路徑 string out_path; //輸出文件路徑 string err_msg; //錯誤日誌 };
首先,在拿到題目後,咱們迅速閱讀了項目的整個要求。在對項目的總體輪廓有大體的瞭解後,咱們開始討論分析了項目的結構。僅僅浮於口上的討論是不夠的,也不利於後續實現。所以咱們草擬了一個文檔初稿來規定了具體分工、接口設計、代碼規範等技術細節問題。
在具體分工方面,雖然是結對編程,但咱們的工做仍有不一樣的側重。根據分工,我主要負責計算核心模塊的開發和異常處理,隊友主要負責測試工做和界面模塊開發。
在接口設計方面,咱們遵守項目要求的接口設計,計算核心模塊和界面模塊都遵守項目要求中的三個接口進行設計。
在代碼技術規範方面,咱們採用《百度C++編程規範》中的要求和建議,做爲咱們的代碼設計規範。
在前期的預備工做準備完畢後,咱們開始上手工做。首先設計了約定的接口並做出簡單的測試,以後我負責開發核心計算模塊,隊友則負責編寫界面模塊和測試用例。因爲事先約定清晰,咱們分別完成首個版本計算模塊和交互模塊後就當即展開了對接,沒有多餘的消耗。
此後我進行了幾輪迭代,完善了計算模塊的全部功能;隊友跟進單元測試和迴歸測試,保證了計算模塊的正確性。最後咱們做了性能分析等後續工做,完成項目。
優勢:可以使代碼處於一種一直在被複審的狀態,程序員不斷審覈對方的代碼,能夠提升編碼質量以及及時發現問題[大機率上]。
缺點:對於迭代快的項目開發,人力資源可能會很緊張, 須要團隊成員獨自開發本身的模塊,結對編程對時間的整體利用率極可能不高。
結對編程最終的效果如何無非就是取決於1. 兩我的的編程水平 2. 兩我的合做的效率。至於博客要求至少列出每一個人三個優勢和一個缺點,我以爲沒啥可寫的。若是兩我的都不鴿對面,而且儘量推動項目的進展,對於結對編程的目的來講,就夠了。