https://github.com/KarCute/Wordlistgit
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | 10 | 30 |
· Estimate | · 估計這個任務須要多少時間 | 10 | 30 |
Development | 開發 | 2565 | 2320 |
· Analysis | · 需求分析 (包括學習新技術) | 120 | 100 |
· Design Spec | · 生成設計文檔 | 50 | 30 |
· Design Review | · 設計複審 (和同事審覈設計文檔) | 30 | 20 |
· Coding Standard | · 代碼規範 (爲目前的開發制定合適的規範) | 15 | 30 |
· Design | · 具體設計 | 90 | 120 |
· Coding | · 具體編碼 | 2000 | 1680 |
· Code Review | · 代碼複審 | 60 | 40 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 200 | 300 |
Reporting | 報告 | 370 | 510 |
· Test Report | · 測試報告 | 120 | 200 |
· Size Measurement | · 計算工做量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 過後總結, 並提出過程改進計劃 | 240 | 300 |
合計 | 2945 | 2860 |
In computer science, 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. The protection involves providing a stable interface which protects the remainder of the program from the implementation (the details that are most likely to change).github
維基百科——Information Hiding
在這裏信息隱藏並非指信息加密或者隱匿,而是一種設計思想,將程序中設計決策中最容易改變的部分分離開來,從而保護其餘部分不受影響。在這裏咱們的設計自始至終都將計算接口剝離開單獨處理,由於這部分自己較難實現,而且在優化代碼算法時也是隻須要對這部分進行改進。算法
Interface除了接口外還有界面的意思,通常說來User Interface Design指的是UI設計。這裏應該指的接口設計。
咱們在設計時是按照指定的接口完成了計算模塊的設計,同時注意到,雖然咱們在整個項目實現時,知足了傳入接口的參數都保證正確,可是對用戶來講,這個接口內部是未知的,他們不必定會按照規範傳入參數,所以須要考慮到設計的完備性,即異常處理。編程
In computing and systems design a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components.數組
耦合是軟件結構中各模塊之間相互鏈接的一種度量,耦合強弱取決於模塊間接口的複雜程度、進入或訪問一個模塊的點以及經過接口的數據。app
百度百科——高內聚低耦合
早在接觸面向對象時就已經知道了好的程序應該作到「高內聚,低耦合」。在咱們的編程中,模塊的接口設計大多傳入參數並不複雜,耦合程度較低。如讀取文件部分,咱們只接受一個文件名,而後就能將單詞存入一個vector容器中。其他模塊只須要接受這個容器,而不須要與該模塊有較多的交互。數據結構和算法
之前作過相似的題,輸入的全部單詞可否所有首尾相連造成鏈。因爲單詞首尾相連有多種鏈接方式,故基本的數據結構爲圖。
建圖有兩種方式,一種是以單詞爲節點,若是單詞間正好能夠首尾鏈接,則添加一條邊,該邊即爲鏈接的字母。另外一種建圖方式是以字母爲節點,以單詞爲邊,出現一個單詞,即把首字母節點向尾字母節點添加一條邊,邊的值即爲該單詞。
對於這道題目而言,因爲單詞須要輸出,加之對第二種建圖方式掌握並不熟練,所以選擇的是第一種建圖方式。
模型確立後,問題就能夠簡化成「求圖中的最長鏈」,即最長路徑問題,顯然問題是多源最長路徑問題。函數
數據結構爲圖,存儲方式爲鄰接矩陣,理由是能更契合floyd算法。
對於無環狀況,因爲爲多源最長路徑問題,聯想到最短路徑問題,能夠肯定爲floyd算法。
而對於有環狀況,因爲出現了正值環,floyd算法再也不適用。在找不到更有解決方法的狀況下,只能適用DFS深度優先搜索求解。oop
ReadFile: 讀取文件的模塊,將文件中的單詞提取進入容器vector中。
Graph: 圖的定義。
InputHandler:處理輸入的模塊,讀取命令行並處理參數。
FindLongestWordList: 計算模塊,內含計算接口。計算出單詞中的最長鏈。
首先須要判斷有無環,對於沒有-r參數的輸入來講,若是有環須要報錯。這裏也是用到DFS的染色算法。每一個點有三種狀態:未遍歷過,遍歷過,當前序列正在遍歷。若是一次DFS中一個點與正在遍歷中的點相連了,說明DFS回到了以前的點,即圖中有環。
另外一問題是因爲無環狀況最多可有10000個單詞,而floyd算法時間複雜度爲O(\(n^3\)),暴力的計算顯然是不行的。考慮到對於無環的狀況,有以下特性:對於單詞element和elephant,因爲無環,這兩個單詞最多隻有一個會出如今鏈中。(不然會出現element, t..., ..., ....e, elephant / element,這樣必定是有環的),而若是要知足字母最多,顯然這時候須要選擇elephant加入鏈中。所以咱們能夠對於全部首尾字母相同的單詞,保留首尾字母組合中,最長的一個單詞。這樣的操做以後,最多的單詞數目爲351,即便是時間複雜度O(\(n^3\))的算法也能很快得出結果。另外能夠計算得,最長鏈的長度最大爲51。
首先是無環狀況,其性能最大阻礙是10000個單詞大樣本狀況下,floyd算法時間複雜度太高致使的。可是在4.4有介紹過,咱們能夠經過無環單詞鏈的特性來削減樣本數量,削減後單詞數量少,即便時間複雜度高也能很快跑出結果。所以性能方面上沒有太大問題。
其次是有環狀況,因爲DFS算法仍屬於暴力遞歸搜索,並不算很好的算法,其性能也着實較差。可是咱們也想不到更好的解決算法,因此並無改進。
契約式設計:定義正式、精確和可驗證的接口規範。
在單元測試部分咱們對程序中除輸出部分外(因爲輸出部分只是一個簡單的輸出到文件)其餘因此部分或函數進行的全面的單元測試,如圖共25個。
TEST_METHOD(TestMethod3) { // TODO: normal_test3 char* words[101] = { "element", "heaven", "table", "teach", "talk"}; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } int l = gen_chain_word(words, 5, answer, 0, 0, true); Assert::AreEqual(l, 4); Assert::AreEqual("table", answer[0]); Assert::AreEqual("element", answer[1]); Assert::AreEqual("teach", answer[2]); Assert::AreEqual("heaven", answer[3]); for (int i = 0; i < 101; i++) { free(answer[i]); } }
TEST_METHOD(TestMethod6) { // TODO: normal_test6 char* words[101] = { "apple", "banane", "cane", "a", "papa", "erase" }; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } int l = gen_chain_char(words, 6, answer, 'a', 'e', false); Assert::AreEqual(l, 3); Assert::AreEqual("a", answer[0]); Assert::AreEqual("apple", answer[1]); Assert::AreEqual("erase", answer[2]); for (int i = 0; i < 101; i++) { free(answer[i]); } }
TEST_METHOD(TestMethod2) { // 正確_2 int argc = 6; char* argv[101] = { "Wordlist.exe", "-r", "-h", "a", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::AreEqual(enable_loop, true); Assert::AreEqual(word_or_char, 2); Assert::AreEqual(head, 'a'); Assert::AreEqual(tail, char(0)); Assert::AreEqual(Filename, (string)"test_1.txt"); }
整體測試思路爲控制變量,即控制是否有首字母、尾字母約束,是否帶環,是最多單詞仍是最多字符。
單元測試覆蓋率截圖(因爲C++沒有找到直接測試單元測試覆蓋率的插件,這裏用的方法是將單元測試代碼移至main函數中用OpenCppCoverage插件獲得的覆蓋率,部分異常測試沒有放進來,因此覆蓋率沒有達到100%)
TEST_METHOD(TestMethod3) { // 錯誤_1 int argc = 5; char* argv[101] = { "Wordlist.exe", "-r", "-r", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; try { InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::IsTrue(false); } catch (myexception1& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是‘-r’出現了兩次,錯誤的參數組合。
TEST_METHOD(TestMethod7) { // 錯誤_5 int argc = 6; char* argv[101] = { "Wordlist.exe", "-r", "-h", "1", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; try { InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::IsTrue(false); } catch (myexception2& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是‘-h’指定首字母爲‘1’,明顯是錯誤的。
TEST_METHOD(TestMethod9) { // 錯誤_7 int argc = 5; char* argv[101] = { "Wordlist.exe", "-b", "-r", "-c", "test_1.txt" }; char head; char tail; bool enable_loop; int word_or_char = 0; string Filename; try { InputHandler(argc, argv, enable_loop, word_or_char, head, tail, Filename); Assert::IsTrue(false); } catch (myexception3& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是輸入參數‘-b’顯然是不符合規定的。
TEST_METHOD(TestMethod2) { // 錯誤 vector <string> words; try { ReadFile("normal_test3.txt", words); // 不存在的文件 Assert::IsTrue(false); } catch (myexception4& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是測試了一個在此路徑下不存在的文件。
TEST_METHOD(TestMethod2) { // 錯誤 vector <string> words; try { ReadFile("long_word_test.txt", words); Assert::IsTrue(false); } catch (myexception4& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是文件中存在長度超過600的單詞。
TEST_METHOD(TestMethod2) { // 錯誤 vector <string> words; try { ReadFile("more_words_test.txt", words); Assert::IsTrue(false); } catch (myexception4& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是所測試文件中單詞數超過了10000。
TEST_METHOD(TestMethod10) { // wrong_test2 char* words[101] = { "alement", "oeaven", "tabla", "teaco", "talk" }; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } try { int l = gen_chain_char(words, 5, answer, 0, 'n', false); Assert::IsTrue(false); } catch (myexception7& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是傳入單詞能夠造成環,且用戶沒有傳入參數‘-r’。
TEST_METHOD(TestMethod11) { // wrong_test3 char* words[101] = { "alement", "oeaven", "tabla", "teaco", "talk" }; char* answer[101]; for (int i = 0; i < 101; i++) { answer[i] = (char*)malloc(sizeof(char) * 601); } try { int l = gen_chain_word(words, 5, answer, 'b', 'n', true); Assert::IsTrue(false); } catch (myexception8& e) { Assert::IsTrue(true); } catch (...) { Assert::IsTrue(false); } }
這個單元測試是規定了首尾字母后,單詞鏈中沒有用戶所要求的單詞鏈。
因爲與隊友爲舍友,結對時相對簡單不少,只須要到對鋪和隊友一塊兒結對編程就好了。咱們的水平差很少, 編程能力和數據結構算法的掌握都不算太好。初期時咱們主要是一塊兒討論算法,如何實現基本的功能,數據結構應該用什麼。敲定一個算法以後就開始分頭找資料,最後再彙總資料,交給他來敲代碼或者我來在一些地方進行修改。編寫時常常會遇到一些意料不到的bug,最後必須一塊兒搜索如何解決。可是兩我的在一塊兒編寫代碼時,有一我的來隨時審視代碼,有不懂的地方或者不對勁的地方另外一人均可以隨時提出來。所以雖然結對編程效率沒有提升, 可是效果會比兩個單人編寫來的更好。
總的來講此次題目難度仍是沒有那麼爆炸,因此咱們之間的合做也比較愉快。至於提意見的藝術是根本用不上的,畢竟是舍友也不會產生矛盾。下面是咱們在初期時討論算法的圖片:
本身的話,優勢是設計時肯思考,能接受對方的意見,願意花時間尋找資料。缺點多是有點愛開小差。 對對方的話,優勢是能主動找資料,能包容個人不足,在我有事時能主動在差很少的測試上花時間編寫。缺點多是和我同樣數據結構和算法掌握不夠好。