[2019BUAA軟件工程]結對做業

[2019BUAA軟件工程]結對做業

寫在前面

Tips Link
做業連接 [2019BUAA軟件工程]結對做業
GitHub地址 WordChain

Personal Software Process 記錄

  如下爲對本次做業各部分用時的估計與實際使用狀況:c++

Personal Software Process Stages 預計耗時(分鐘) 實際耗時(分鐘)
Planning Estimate 20 20
Total 20 20
Development Analysis 60 60
Design Spec 60 50
Design Review 40 40
Coding Standard 20 15
Design 120 300
Coding 360 600
Code Review 120 100
Test 420 600
Total 1200 1600
Reporting Test Report 90 120
Size Measurement 30 40
Postmortem & Process Improvement Plan 30 40
Total 180 200
Total 1400 1820

模塊化設計思想

信息隱藏(Information Hiding)

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).git

  簡而言之,信息隱藏就是在設計與實現模塊時,限制其餘模塊對該模塊內包含的特定信息的訪問進行限制。知足信息隱藏的模塊須要自行維護內部的數據結構和運算邏輯,而且要保證不會由於外部的操做而影響內部的全部邏輯結構的運行,使自身處於在可控制的範圍,具備更高的穩定性。
  對於整個計算模塊Core,外層(控制檯、GUI)沒法對其中的數據結構進行操做,保證了計算模塊與外層的信息隱藏。計算模塊Core內部的數據類WordList和運算邏輯類Solve/DPSolve之間也存在信息隔離。數據類沒有操做運算邏輯類的權限;運算邏輯類只能經過數據類提供的合法接口間接操做、獲取數據。github

接口設計(Interface Design)

Good user interface design facilitates finishing the task at hand without drawing unnecessary attention to itself.算法

  對於模塊接口的設計應當使外層只關注於模塊雖提供的功能、接口的輸入以及產生的效果,而非模塊內層的實現邏輯。接口的做用體如今對代碼進行修改的時候:在修改某一模塊的內部邏輯時,只需保證對外接口不變就無需修改外層代碼。合適的接口設計提升了工程的可重構性、可延展性,下降了維護的難度。編程

  對於計算模塊Core的接口,外層只需考慮輸入計算的單詞集合以及相應的運算模式參數就可以得到最終結果或者錯誤提示。對於數據類提供的接口,其餘模塊只需按照其須要獲取的信息選擇不一樣的接口函數並輸入參數就能獲取到搜索結果。後端


下降耦合(Loose Coupling)

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. Subareas include the coupling of classes, interfaces, data, and services.Loose coupling is the opposite of tight coupling.數組

  模塊間的耦合度是指模塊之間的依賴關係,包括控制關係、調用關係、數據傳遞關係。耦合的強弱取決於模塊間接口的複雜性、調用模塊的方式以及經過界面傳送數據的多少。模塊間聯繫越多,其耦合性越強,同時代表其獨立性越差。數據結構

  計算模塊中的耦合主要體如今運算模塊和單詞數據存儲模塊。因爲運算模塊是基於單詞數據模塊構建的,運算模塊的不少函數須要適應於單詞模塊提供的接口。運算模塊依賴於單詞模塊是較難避免的。但單詞模塊對運算模塊的依賴就較低。單詞模塊只需完成自身數據維護和提供查詢接口便可,無需依賴其餘模塊。於是在計算模塊中,單詞模塊的設計使其能複用給兩個運算模塊做爲數據源。多線程


計算模塊接口的設計與實現過程

對外接口設計:

  • 接口:
int gen_chain_word(char* words[], int len, char* output, char head, char tail, bool enable_loop);
int gen_chain_char(char* words[], int len, char* output, char head, char tail, bool enable_loop);
  • 參數解釋:
    • char* words[]:傳入單詞集合。
    • char* output[]:輸出單詞鏈。
    • int len:輸入單詞集合長度。
    • char head:單詞鏈首字母限制。無限制輸入0,有限制則輸入相應英文字符。
    • char tail:單詞鏈首字母限制。無限制輸入0,有限制則輸入相應英文字符。
    • bool enable_loop:單詞環限制。容許存在單詞換爲true,不然爲false
    • 返回值:返回搜得最長單詞鏈長度或錯誤碼。

類功能設計:

  1. Core:內外層交互類
    • 實現計算模塊與外層(Console或GUI模塊)交互邏輯。
    • 接收外層的原始數據和相應參數。
    • 對接收到的原始數據進行預處理:
      • 從原始數據中提取出單詞。
      • 過濾重複單詞。
      • 將有效單詞按首尾字母歸類,存入單詞庫中。
      • 更新單詞庫中單詞連接矩陣。
    • 按照指定模式組織內層計算邏輯。
    • 返回內層計算結果。
  2. Solve/DPSolve:核心計算類
    • 實現對已有單詞庫搜索單詞鏈的算法。
    • 核心算法:優化BFS(有單詞環)和動態規劃(無單詞環)。
    • 輔助算法:拓撲排序(檢查是否存在單詞環)。
  3. WordList:數據類
    • 格式化存儲單詞。
    • 將單詞連接關係組織爲鄰接矩陣。
    • 提供相應的查詢函數。

類流程關係

  1. Core獲取原始數據與運行參數。
  2. Core預處理原始數據,生成單詞庫(WordList)。
  3. 按照參數啓動Solve/DPSolve相應計算函數。
  4. 完成單詞鏈搜索,Core輸出計算結果。

類關鍵函數設計及實現

  1. Core:
    • 關鍵函數:
      • gen_chain_word():按單詞數量搜索單詞鏈時的對外接口。
      • gen_chain_char():按單詞長度搜索單詞鏈時的對外接口。
  2. WordList:
    • 存儲結構設計思路:
      1. 單詞表組織:
          將消除重複後的單詞按照首尾字母進行歸類。好比:apple歸類爲ae類單詞。爲每一種類的單詞創建數組存儲,每一個數組內的同種單詞按照長度降序排列。對存儲每一種類單詞的數組按照HASH表存儲至一維數組中。
      2. 輔助結構組織:
          根據已經創建的單詞表,將字母視爲節點,單詞視爲由首字母結點指向尾字母的有向邊,則可以將單詞表組織爲有向圖。使用位圖(BitMap)存儲鄰接矩陣,使用HASH數組存儲各結點的入度。
      3. 類中的索引均採用HASH函數:Index = 首字母相對'a'偏移 * 26 + 尾字母相對'a'偏移
    • 成員變量:
      • m_list:單詞表,按照首尾字母的種類對單詞進行分組儲存,存儲採用HASH結構,不存在重複單詞,同種類單詞按照長度由長至短排列。
      • m_iListSize:單詞表長度,存儲不一樣首尾字母的單詞數量。
      • m_iListGetPoint:單詞檢索索引,存儲已檢索的單詞的索引。
      • m_iArryMatrix[]:單詞鏈鄰接矩陣。
      • m_iArrayNodeIn[]:結點入度數組。
    • 關鍵函數:
      • parseString()
        • 解析字符串,提取單詞。
        • 調用私有函數addWord添加單詞。
      • getWordAt()
        • 獲取符合首尾條件的單詞。
      • getWordSumAt()
        • 獲取符合首尾條件的單詞總數量
      • getNodeIn()
        • 獲取單詞鏈圖某一首字母結點的入度。
      • getNodeOut()
        • 獲取單詞鏈圖某一首字母結點的出度。
      • getNodeNext()
        • 獲取單詞鏈圖某一首字母結點的鄰接矩陣項。
      • addWord()"private"
        • 過濾重複單詞,將有效單詞添加至單詞表相應分類。
        • 保證添加後各分類內單詞長度降序排列。
  3. Solve:
    • 設計思路
        使用深度優先搜索加剪枝策略,解決了頭尾限定問題,以及屬於單詞序列中存在環時得問題。
    • 成員變量:
      • m_iSigned26: 標記兩個字母之間的路徑是否以及走過
      • m_ihead[26]: 標記每一個字母是否以及用過
      • m_ans676: 記錄單詞是否使用的變量
      • next_tag26: 儲存字母鄰接鏈表
      • max_dfs[26]: 記錄每一個單詞距離終點的最大距離
      • final_dfs[26] 記錄最終的每一個字母節點距離其重點的距離
      • m_FinalLen: 單詞鏈最終長度
      • m_TemLen: 搜索到的當前字符鏈的長度
      • max_num: 單詞鏈個數最大長度
      • temp_num 搜索到的單詞鏈表當前個數
      • bool is_circle: 判斷是否有環
      • head: 肯定頭部字母記錄下的頭部字母
      • char m_Mode: 記錄搜索模式的變量
      • char m_ModeHead: 記錄搜索的時指定的頭部
      • char m_ModeTail: 記錄搜索的時指定的尾部
      • bool m_ModeRing: 記錄搜索模式是否有環
      • vector m_FinalChain: 最終結果
      • vector m_TempChain: 當前路徑
    • 關鍵方法
      • Solve1(): 解決容許有環時的搜索問題
      • Solve2_he(): 解決無環時的指定頭尾的搜索問題
      • Dfs_solve1(): 解決容許有環時的搜索問題的搜索函數
      • cmp(): 容許有環的時候對當前最大長度的更新函數
      • printChain(): 容許有環的時候的輸出函數
      • Dfs_solvehe(): 指定頭尾問題的搜索問題的遞歸搜索函數
      • cmp_he(): 解決頭尾指定問題時候 對當前最大長度進行更新的函數
      • printhe(): 解決頭尾指定問題時候 的輸出問題
  4. DPSolve:
    • 實現思路:
      • 使用動態規劃解決無單詞環狀況下除同時限定單詞鏈首尾的問題。根據限制條件的不一樣採起正向或逆向動態規劃。
    • 動態規劃:
      • 正向遞推式:F(c1)=max{F(c1)+len(c1ci)}F(c1)=max{F(c1)+len(c1ci)}
      • 逆向遞推式:f(c1)=max{f(c1)+len(cic1)}f(c1)=max{f(c1)+len(cic1)}
      • F(c)爲由c開始的最長單詞鏈長度。
      • f(c)爲以c結束的最長單詞鏈長度。
      • len(cicj)是以ci爲首以cj爲尾的最長單詞長度。
    • 單詞環檢測:
      • 使用拓撲排序算法檢測單詞環,保證動態規劃正確進行。
    • 成員變量:
      • m_ptrWordList:單詞表指針。
      • m_cMode:搜索模式,分爲按數量和按程度。
      • m_cModeHead:頭部限定。
      • m_cModeTail:尾部限定。
      • m_iArrayDp[]:動態規劃結果。
      • m_iArrayNext[]:存儲相應結點的最優後繼結點。
      • m_iArrayBefore[]:存儲相應結點的最優前綴結點。
    • m_iQueueTopo:拓撲排序結果。
      • m_strVecWordChain:存儲計算獲得的單詞鏈。
    • 關鍵方法:
      • DPSolve():構造函數,傳入單詞表和各類參數。
      • startDPSolve():啓動函數。
      • topoSort():拓撲排序,檢查單詞表中是否有環。
      • getWordChain():導出計算後的結果。
      • DPStep(int indexH):正向動態規劃子函數,私有函數。
      • DPStepRe(int indexH):逆向動態規劃子函數,私有函數。

UML設計


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

  • 第一階段:深度搜索實現基本功能

  筆者在最初進行程序設計時採起的是使用簡潔的方法先實現需求的全部功能,再根據實際運行狀況優化算法。根據這樣的初衷產生了以下的流程設計。
  首先,將全部單詞按照首尾字母進行分類。以字母爲結點,以單詞爲有向邊(由單詞首字母結點指向尾字母節點),將單詞表構形成圖結構。如此組織單詞表能夠很好地對需求中的問題進行簡化和抽象:無單詞環時,很容易證實每一個結點間最多單向通過一次。好比'a'、'b'兩結點最多僅能有一個有向邊。因而即可以將問題轉化爲在構造的圖中搜索最長路徑。對於‘-w’功能則是將邊權重設置爲‘1’;對於‘-c’功能則是將邊權重設置爲該邊對應類型單詞中最長的單詞長度;而‘-t’和‘-h’功能則是增長了首尾節點的限制。而對於有單詞環的狀況,每一個結點間可通過的方向及其次數等於其所對應種類的單詞數量,運用深度搜索也一樣可以解決相應的問題。
  在搜索階段,程序對每一個節點進行深度搜索,搜索以該結點爲首的最長路徑,並根據不一樣的輸入參數得出符合要求的單詞鏈。最終經過接口完成結果的輸出。app

  • 第二階段:動態規劃優化部分功能

  筆者的小組在較短期內就完成了基於深度搜索的單詞鏈搜索程序。在經過正確性測試並進行性能測試後,不出意料的出現的運行效率低的問題。運行效率低的主要緣由是原始深度遍歷不具備記憶和預測功能。這會致使在進行屢次遍歷時會對以搜索的路徑和明顯不是最優解的路徑進行遍歷。會通過分析後,咱們決定採起如下方案對程序進行優化。
  對於無單詞環而且不一樣時限定首尾的狀況,廢棄原先的方法轉而採用動態規劃的方法。這幾個搜索最長單詞鏈的問題能夠分解爲求解與其相連的字母的最長單詞鏈加上二者間距離的最大值。好比正向動態規劃時,求解以'a'爲首的最長單詞鏈,而'b'、'c'結點能夠由'a'結點直接到達,則原問題的最優解爲子問題'b'、'c'的最優解分別加上'a'到這些結點的邊的權值。用公式表示爲:
正向遞推式:F(c1)=max{F(c1)+len(c1ci)}*或*F(c1)=max{F(c1)+len(c1ci)}
逆向遞推式:f(c1)=max{f(c1)+len(cic1)}*或*f(c1)=max{f(c1)+len(cic1)}
  在使用動態規劃後,程序將無單詞環而且不一樣時限定首尾的狀況的計算壓縮至了1s內,基本知足了要求。

  • 第三階段:優化深度搜索

  在進行動態規劃的優化以後,咱們發現,動態規劃雖然效率很高,可是並不能處理同時指定頭和尾的問題,因而咱們仿照動態規劃表達式的思想,對於咱們的深度優先搜索作了一步剪枝。具體的剪枝思想以下:
  在搜索回退的時候,用一個數組,記錄下每一個字母距離其末尾的最大長度,或者最大單詞個數,當第二次搜索到這個字母節點時,直接比較當前長度,當前節點距離末尾的最大長度,若是前者比後者小,直接剪掉這個分支。保證了每條邊只搜索了一遍,極大的提升了深度優先搜索的效率。

  • 性能測試:

  上圖爲在無單詞環的狀況下進行的約九千單詞量的性能測試結果。由圖可知,在進行vector操做上的消耗較多。消耗最多的代碼是在addWord函數中將新單詞加入至有序單詞表的操做時採用了vector的insert函數。這一消耗僅出如今預處理的部分,在保證單詞表中的單詞是按照長度降序排列的同時還過濾了重複的單詞。該操做雖然帶來了必定的損耗,可是組織好的單詞表更易於計算過程的檢索,下降了檢索的複雜度,防止遞歸致使的消耗的放大。計算過程採用的動態規劃算法大大減少了該階段的消耗。

  上圖爲在有單詞環的狀況下進行的性能測試。CPU的消耗主要出在遞歸調用的函數Dfs_solve1中。


契約式設計體現

契約式編程 & 防護式編程

  提到契約式編程就不能不說防護式編程。在實現某一模塊時,對於異常輸入的處理通常有如下兩種處理方式:

  1. 兼容處理全部狀況,保證程序正確執行完成。
  2. 識別並拋出不符合要求的狀況,告知外層找出錯誤緣由。

  前者就是防護是編程思想,後者就是契約式編程思想。契約式編程思想是在每一個模塊間創建明確的「責任」和「義務」關係。每一個模塊在使用其餘模塊的接口時都有責任保證使用的正確性,即符合接口的先驗條件等;而在知足了先驗條件的狀況下,被調用模塊有義務保證執行功能的正確性,即知足接口的後驗條件和不變式等。契約的內容常體現於如下方面(來自維基百科):

  • 可接受和不可接受的值或類型,以及它們的含義
  • 返回的值或類型,以及它們的含義
  • 可能出現的錯誤以及異常狀況的值和類型,以及它們的含義
  • 反作用
  • 先驗條件
  • 後驗條件
  • 不變條件
  • 性能上的保證,如所用的時間和空間

簡析契約式編程:

  • 優勢:
    1. 簡化代碼
      防護式編程須要在實現正常功能之餘,爲防護外來的不合法狀況進行判斷和處理,以保證功能的正常運行。所以須要在每一個子功能內增長大量的代碼。而契約式編程因爲有先驗條件,被調用者減小了處理的代碼,使每一個子功能的邏輯簡潔而清晰,實現每一個子功能代碼的輕量化。
    2. 加強程序的健壯性
      在必定程度上簡化代碼就能大大減少Bug的出現,尤爲是防護代碼的Bug。除此以外,契約可以明確地劃分系統內各個部分職責,下降各模塊的耦合程度,使程序有更好的魯棒性和可維護性。
  • 缺點:
    1. 對契約完備性的要求高
      在契約式編程帶來好處的同時,其對契約的完備性的要求較高。當契約存在漏洞的時候,每一個模塊不能想防護式編程對異常狀況進行兼容式處理,於是極易致使程序出現意想不到的情況。契約式編程的優勢成立的前提是有一個完備的契約系統
    2. 權責劃分難度較大
      理想的契約式編程中,調用者和被調用者應當是平等的:各自都有自身的權利和義務,而且模塊的權責是均衡的。但在設計階段對模塊的權責進行劃分時要保證均衡地爲每一個模塊劃分權責的難度較大。這須要對整個程序有較全面的考量,而非從局部功能來設計模塊的契約。

契約式編程的應用

  • 單詞表接口契約:
      其餘模塊在調用單詞表模塊的接口使須要保證傳入的參數在相應的範圍內(先驗條件)。在知足先驗條件的狀況下,單詞表模塊保證能返回正確的查詢結果,同時不破壞現有的單詞表。

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

  筆者對工程中Core類的正確性以及封裝後的dll的正確性進行了測試

  • Core類測試集
TEST_CLASS(TEST)
{
public:

Core *CORE = NULL;

TEST_METHOD_INITIALIZE(initEnv)
{
    CORE = new Core();
}

TEST_METHOD_CLEANUP(cleanWordList)
{
    delete CORE;
    CORE = NULL;
}

TEST_METHOD(Test_1)             // -w
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bd",
        "cd",
    };
    string ans[] = {
        "ab", "bc", "cd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for(int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 0, 0, false));
    Assert::AreEqual(size_t(3), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    Assert::AreEqual(ans[2], (*chain)[2]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_2)             // -c
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bd",
        "cd",
    };
    string ans[] = {
        "accccccccccccccccccccc", "cd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 0, 0, false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_3)             // -w -h
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
    };
    string ans[] = {
        "bc", "cd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 'b', 0, false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_4)             // -c -h
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
        "dd",
    };
    string ans[] = {
        "bdddd", "dd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 7; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 'b', 0, false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_5)             // -w -t
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
    };
    string ans[] = {
        "ab", "bc"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 0, 'c', false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_6)             // -c -t
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
        "cc"
    };
    string ans[] = {
        "accccccccccccccccccccc", "cc"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 7; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 0, 'c', false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_7)             // -w -h -t
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "be",
        "cd",
        "ce",
        "de"
    };
    string ans[] = {
        "bc", "cd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 10; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 'b', 'd', false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_8)             // -c -h -t
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "be",
        "cd",
        "ce",
        "de",
        "dd",
    };
    string ans[] = {
        "bdddd", "dd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 11; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 'b', 'd', false));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_9)             // -w  but have ring
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "db",
        "be",
        "cd",
        "ce",
        "de"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 11; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(-2, CORE->gen_chain_word(*lines, *chain, 0, 0, false));
    delete lines;
    delete chain;
}

TEST_METHOD(Test_10)                // -c but have ring
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "db",
        "be",
        "cd",
        "ce",
        "de"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 11; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(-2, CORE->gen_chain_char(*lines, *chain, 0, 0, false));
    delete lines;
    delete chain;
}

TEST_METHOD(Test_11)                // -w have self
{
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bb",
        "bc",
        "bdddd",
        "be",
        "cc",
        "cd",
        "ce",
        "dd",
        "de"
    };
    string ans[] = {
        "ab", "bb", "bc", "cc", "cd", "dd", "de"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 13; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 0, 0, false));
    Assert::AreEqual(size_t(7), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    Assert::AreEqual(ans[2], (*chain)[2]);
    Assert::AreEqual(ans[3], (*chain)[3]);
    Assert::AreEqual(ans[4], (*chain)[4]);
    Assert::AreEqual(ans[5], (*chain)[5]);
    Assert::AreEqual(ans[6], (*chain)[6]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_12){               // -c have self
    string words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bb",
        "bc",
        "bdddd",
        "be",
        "cc",
        "cd",
        "ce",
        "dd",
        "de"
    };
    string ans[] = {
        "accccccccccccccccccccc", "cc", "cd", "dd", "de"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 13; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 0, 0, false));
    Assert::AreEqual(size_t(5), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    Assert::AreEqual(ans[2], (*chain)[2]);
    Assert::AreEqual(ans[3], (*chain)[3]);
    Assert::AreEqual(ans[4], (*chain)[4]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_13)                // -w -r
{
    string words[] = {
        "ab", "aaaaabbbbbccccc", "aaaaaddddd",
        "bc",
        "cb", "cd",
    };
    string ans[] = {
        "aaaaabbbbbccccc", "cb", "bc", "cd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 0, 0, true));
    Assert::AreEqual(size_t(4), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    Assert::AreEqual(ans[2], (*chain)[2]);
    Assert::AreEqual(ans[3], (*chain)[3]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_14) {              // -c -r
    string words[] = {
        "ab", "aaaccc", "aaaaabbbbbcccccddddd",
        "bc",
        "cb", "cd",
        "dd",
    };
    string ans[] = {
        "aaaaabbbbbcccccddddd", "dd"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 7; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 0, 0, true));
    Assert::AreEqual(size_t(2), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_15) {              // -w -r -h -t
    string words[] = {
        "ab",
        "bccccccccccccccccccccccccccc", "bd",
        "cd",
        "da", "dc",
    };
    string ans[] = {
        "bd", "dc", "cd", "da"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_word(*lines, *chain, 'b', 'a', true));
    Assert::AreEqual(size_t(4), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    Assert::AreEqual(ans[2], (*chain)[2]);
    Assert::AreEqual(ans[3], (*chain)[3]);
    delete lines;
    delete chain;
}

TEST_METHOD(Test_16) {              // -c -r -h
    string words[] = {
        "ab",
        "bccccccccccccccccccccccccccc", "bd",
        "cd",
        "da", "dc",
    };
    string ans[] = {
        "bccccccccccccccccccccccccccc","cd", "da"
    };
    vector<string> *lines = new vector<string>();
    vector<string> *chain = new vector<string>();
    for (int i = 0; i < 6; i++)
    {
        lines->push_back(words[i]);
    }
    Assert::AreEqual(0, CORE->gen_chain_char(*lines, *chain, 'b', 'a', true));
    Assert::AreEqual(size_t(3), chain->size());
    Assert::AreEqual(ans[0], (*chain)[0]);
    Assert::AreEqual(ans[1], (*chain)[1]);
    Assert::AreEqual(ans[2], (*chain)[2]);
    delete lines;
    delete chain;
}
  • 拓撲排序測試
TEST_METHOD(TEST_TopoSort_1)
{
    string str_list[] = {
        "aa", "abc" , "cbd", "ddd", "da"
    };
    for (int i = 0; i < 5; i++)
    {
        WORDLIST->parseString(str_list[i]);
    }
    dpSolve = new DPSolve(WORDLIST, 'w');
    Assert::AreEqual(false, dpSolve->topoSort());
}

TEST_METHOD(TEST_TopoSort_2)
{
    string str_list[] = {
        "aa", "abc" , "cbd", "ddd", "db"
    };
    for (int i = 0; i < 5; i++)
    {
        WORDLIST->parseString(str_list[i]);
    }
    dpSolve = new DPSolve(WORDLIST, 'w');
    Assert::AreEqual(true, dpSolve->topoSort());
}
  • dll測試
TEST_METHOD(Test_1)             // -w
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bd",
        "cd",
    };
    char* chain[100];
    char* ans[] = {
        "ab", "bc", "cd"
    };
    Assert::AreEqual(3, gen_chain_word(words, 6, chain, 0, 0, false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
    Assert::AreEqual(0, strcmp(chain[2], ans[2]));
}

TEST_METHOD(Test_2)             // -c
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bd",
        "cd",
    };
    char* chain[100];
    char* ans[] = {
        "accccccccccccccccccccc", "cd"
    };
    Assert::AreEqual(2, gen_chain_char(words, 6, chain, 0, 0, false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_3)             // -w -h
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
    };
    char* chain[100];
    char* ans[] = {
        "bc", "cd"
    };
    Assert::AreEqual(2, gen_chain_word(words, 6, chain, 'b', 0, false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_4)             // -c -h
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
        "dd",
    };
    char* chain[100];
    char* ans[] = {
        "bdddd", "dd"
    };
    Assert::AreEqual(2, gen_chain_char(words, 7, chain, 'b', 0, false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_5)             // -w -t
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
    };
    char* chain[100];
    char* ans[] = {
        "ab", "bc"
    };
    Assert::AreEqual(2, gen_chain_word(words, 6, chain, 0, 'c', false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_6)             // -c -t
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "bc",
        "bdddd",
        "cd",
        "cc"
    };
    char* chain[100];
    char* ans[] = {
        "accccccccccccccccccccc", "cc"
    };
    Assert::AreEqual(2, gen_chain_char(words, 7, chain, 0, 'c', false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_7)             // -w -h -t
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "be",
        "cd",
        "ce",
        "de"
    };
    char* chain[100];
    char* ans[] = {
        "bc", "cd"
    };
    Assert::AreEqual(2, gen_chain_word(words, 10, chain, 'b', 'd', false));

    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_8)             // -c -h -t
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "be",
        "cd",
        "ce",
        "de",
        "dd",
    };
    char* chain[100];
    char* ans[] = {
        "bdddd", "dd"
    };
    Assert::AreEqual(2, gen_chain_char(words, 11, chain, 'b', 'd', false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_9)             // -w  but have ring
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "db",
        "be",
        "cd",
        "ce",
        "de"
    };
    char* chain[100];
    Assert::AreEqual(-4, gen_chain_word(words, 11, chain, 0, 0, false));
}

TEST_METHOD(Test_10)                // -c but have ring
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bc",
        "bdddd",
        "db",
        "be",
        "cd",
        "ce",
        "de"
    };
    char* chain[100];
    Assert::AreEqual(-4, gen_chain_char(words, 11, chain, 0, 0, false));
}

TEST_METHOD(Test_11)                // -w have self
{
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bb",
        "bc",
        "bdddd",
        "be",
        "cc",
        "cd",
        "ce",
        "dd",
        "de"
    };
    char* chain[100];
    char* ans[] = {
        "ab", "bb", "bc", "cc", "cd", "dd", "de"
    };
    Assert::AreEqual(7, gen_chain_word(words, 13, chain, 0, 0, false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
    Assert::AreEqual(0, strcmp(chain[2], ans[2]));
    Assert::AreEqual(0, strcmp(chain[3], ans[3]));
    Assert::AreEqual(0, strcmp(chain[4], ans[4]));
    Assert::AreEqual(0, strcmp(chain[5], ans[5]));
    Assert::AreEqual(0, strcmp(chain[6], ans[6]));
}

TEST_METHOD(Test_12) {              // -c have self
    char* words[] = {
        "ab",
        "accccccccccccccccccccc",
        "ad",
        "ae",
        "bb",
        "bc",
        "bdddd",
        "be",
        "cc",
        "cd",
        "ce",
        "dd",
        "de"
    };
    char* chain[100];
    char* ans[] = {
        "accccccccccccccccccccc", "cc", "cd", "dd", "de"
    };
    Assert::AreEqual(5, gen_chain_char(words, 13, chain, 0, 0, false));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
    Assert::AreEqual(0, strcmp(chain[2], ans[2]));
    Assert::AreEqual(0, strcmp(chain[3], ans[3]));
    Assert::AreEqual(0, strcmp(chain[4], ans[4]));
}

TEST_METHOD(Test_13)                // -w -r
{
    char* words[] = {
        "ab", "aaaaabbbbbccccc", "aaaaaddddd",
        "bc",
        "cb", "cd",
    };
    char* chain[100];
    char* ans[] = {
        "aaaaabbbbbccccc", "cb", "bc", "cd"
    };
    Assert::AreEqual(4, gen_chain_word(words, 6, chain, 0, 0, true));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
    Assert::AreEqual(0, strcmp(chain[2], ans[2]));
    Assert::AreEqual(0, strcmp(chain[3], ans[3]));
}

TEST_METHOD(Test_14) {              // -c -r
    char* words[] = {
        "ab", "aaaccc", "aaaaabbbbbcccccddddd",
        "bc",
        "cb", "cd",
        "dd",
    };
    char* ans[] = {
        "aaaaabbbbbcccccddddd", "dd"
    };
    char* chain[100];
    Assert::AreEqual(2, gen_chain_char(words, 7, chain, 0, 0, true));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
}

TEST_METHOD(Test_15) {              // -w -r -h -t
    char* words[] = {
        "ab",
        "bccccccccccccccccccccccccccc", "bd",
        "cd",
        "da", "dc",
    };
    char* ans[] = {
        "bd", "dc", "cd", "da"
    };
    char* chain[100];
    Assert::AreEqual(4, gen_chain_word(words, 6, chain, 'b', 'a', true));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
    Assert::AreEqual(0, strcmp(chain[2], ans[2]));
    Assert::AreEqual(0, strcmp(chain[3], ans[3]));
}

TEST_METHOD(Test_16) {              // -c -r -h
    char* words[] = {
        "ab",
        "bccccccccccccccccccccccccccc", "bd",
        "cd",
        "da", "dc",
    };
    char* ans[] = {
        "bccccccccccccccccccccccccccc","cd", "da"
    };
    char* chain[100];
    Assert::AreEqual(3, gen_chain_char(words, 6, chain, 'b', 'a', true));
    Assert::AreEqual(0, strcmp(chain[0], ans[0]));
    Assert::AreEqual(0, strcmp(chain[1], ans[1]));
    Assert::AreEqual(0, strcmp(chain[2], ans[2]));
}

  如下是測試時代碼覆蓋率及測試結果。


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

  • 異常種類:
    • 輸入輸出單詞表有效性
    • 參數(head、tail)有效性
    • 無環狀況搜索到環
    • 未搜索到單詞鏈
  • 異常測試:
    • 輸入輸出單詞表有效性測試
    TEST_METHOD(Test_1)
        {
          char* words[] = {
            NULL,
            NULL,
            "ad",
            "bc",
            "bd",
            "cd",
          };
          char* chain[100];
          Assert::AreEqual(-3, gen_chain_word(words, 6, chain, 0, 0, false));
        }
    • 參數有效性測試
    TEST_METHOD(Test_2)
          {
              char* words[] = {
                  "ab",
                  "accccccccccccccccccccc",
                  "ad",
                  "bc",
                  "bd",
                  "cd",
              };
              char* chain[100];
              Assert::AreEqual(-1, gen_chain_word(words, 6, chain, '-', 0, false));
          }
    
          TEST_METHOD(Test_3)
          {
              char* words[] = {
                  "ab",
                  "accccccccccccccccccccc",
                  "ad",
                  "bc",
                  "bd",
                  "cd",
              };
              char* chain[100];
              Assert::AreEqual(-2, gen_chain_word(words, 6, chain, 0, '-', false));
          }
    • 環識別測試
    TEST_METHOD(Test_6)
          {
              char* words[] = {
                  "ab",
                  "accccccccccccccccccccc",
                  "ba",
              };
              char* chain[100];
              Assert::AreEqual(-4, gen_chain_word(words, 3, chain, 0, 0, false));
          }
    • 未搜索到單詞鏈測試
    TEST_METHOD(Test_4)
          {
              char* words[] = {
                  "ab",
                  "accccccccccccccccccccc",
                  "ad",
              };
              char* chain[100];
              Assert::AreEqual(-5, gen_chain_word(words, 3, chain, 0, 0, false));
          }

界面模塊設計

  界面模塊設計主要採用C++的 QT模塊,利用QT creator 先設計好主要的GUI界面以後,導出代碼,而後針對每一個控件編寫相應的響應函數。GUI 實在64位的編譯環境下編寫,主要支持功能時直接輸入框輸入單詞,和用戶交互式的導入文本文件,也支持將程序運行的結果導出到用戶指定文件中。

  模塊主要界面以下:

  其中主要的功能函數綁定在openfile writefile 以及 run 三個Button 上,界面代碼以下:

void gui(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QStringLiteral("MainWindow"));
        MainWindow->resize(661, 397);
        centralWidget = new QWidget(MainWindow);
        centralWidget->setObjectName(QStringLiteral("centralWidget"));
        //centralWidget = MainWindow;
        textBrowser = new QTextBrowser(centralWidget);
        textBrowser->setObjectName(QStringLiteral("textBrowser"));
        textBrowser->setGeometry(QRect(300, 50, 201, 221));
        textBrowser_2 = new QTextEdit(centralWidget);
        textBrowser_2->setObjectName(QStringLiteral("textBrowser_2"));
        textBrowser_2->setGeometry(QRect(10, 50, 201, 221));
        checkBox = new QCheckBox(centralWidget);
        checkBox->setObjectName(QStringLiteral("checkBox"));
        checkBox->setGeometry(QRect(540, 40, 71, 16));
        checkBox_2 = new QCheckBox(centralWidget);
        checkBox_2->setObjectName(QStringLiteral("checkBox_2"));
        checkBox_2->setGeometry(QRect(540, 60, 71, 16));
        checkBox_3 = new QCheckBox(centralWidget);
        checkBox_3->setObjectName(QStringLiteral("checkBox_3"));
        checkBox_3->setGeometry(QRect(540, 80, 71, 16));
        checkBox_4 = new QCheckBox(centralWidget);
        checkBox_4->setObjectName(QStringLiteral("checkBox_4"));
        checkBox_4->setGeometry(QRect(540, 100, 71, 16));
        checkBox_5 = new QCheckBox(centralWidget);
        checkBox_5->setObjectName(QStringLiteral("checkBox_5"));
        checkBox_5->setGeometry(QRect(540, 20, 71, 16));
        textEdit = new QLineEdit(centralWidget);
        textEdit->setObjectName(QStringLiteral("textEdit"));
        textEdit->setGeometry(QRect(540, 140, 30, 30));
        textEdit_2 = new QLineEdit(centralWidget);
        textEdit_2->setObjectName(QStringLiteral("textEdit_2"));
        textEdit_2->setGeometry(QRect(580, 140, 30, 30));
        label = new QLabel(centralWidget);
        label->setObjectName(QStringLiteral("label"));
        label->setGeometry(QRect(540, 120, 31, 20));
        label_2 = new QLabel(centralWidget);
        label_2->setObjectName(QStringLiteral("label_2"));
        label_2->setGeometry(QRect(580, 120, 31, 20));
        pushButton = new QPushButton(centralWidget);
        pushButton->setObjectName(QStringLiteral("pushButton"));
        pushButton->setGeometry(QRect(70, 10, 75, 23));
        pushButton_2 = new QPushButton(centralWidget);
        pushButton_2->setObjectName(QStringLiteral("pushButton_2"));
        pushButton_2->setGeometry(QRect(360, 10, 75, 23));
        pushButton_3 = new QPushButton(centralWidget);
        pushButton_3->setObjectName(QStringLiteral("pushButton_3"));
        pushButton_3->setGeometry(QRect(220, 130, 75, 23));
        MainWindow->setCentralWidget(centralWidget);
        menuBar = new QMenuBar(MainWindow);
        menuBar->setObjectName(QStringLiteral("menuBar"));
        menuBar->setGeometry(QRect(0, 0, 661, 23));
        MainWindow->setMenuBar(menuBar);
        mainToolBar = new QToolBar(MainWindow);
        mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
        MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
        statusBar = new QStatusBar(MainWindow);
        statusBar->setObjectName(QStringLiteral("statusBar"));
        MainWindow->setStatusBar(statusBar);
        retranslateUi(MainWindow);

        QMetaObject::connectSlotsByName(MainWindow);
    } // setupUi

openfie

  該 button 綁定了導入輸入文件的函數,能夠方便讓用戶直接導入本身的輸入數據,代碼實現以下:

void MainWindow::openfile()
{

  QString fileName = QFileDialog::getOpenFileName(this,tr("choose log"),"",tr("TXT(*.txt)"));
  if (fileName.isEmpty())
      return;
  QFile file(fileName);
  if (file.open(QIODevice::ReadOnly | QIODevice::Text))
  {
      while (!file.atEnd())
      {

          QByteArray line = file.readLine();
          QString str(line);

          //qDebug() << str;;
          this->textBrowser_2->insertPlainText(str);
      }

      file.close();
  }

}

writefile

  該 button 綁定了導入結果數據的函數,能夠方便讓用戶直接導出函數的運行結果,代碼實現以下:

void MainWindow::writefile()
{

  QString fileName = QFileDialog::getOpenFileName(this,tr("choose log"),"",tr("TXT(*.txt)"));
  if (fileName.isEmpty())
      return;
  QFile file(fileName);
  if (file.open(QIODevice::ReadOnly | QIODevice::Text))
  {
      while (!file.atEnd())
      {

          QByteArray line = file.readLine();
          QString str(line);

          //qDebug() << str;;
          this->textBrowser_2->insertPlainText(str);
      }

      file.close();
  }

}

run

  該button綁定的函數調用了Core.dll中的函數,實現了對數據的處理與計算。代碼以下:

void MainWindow::run()
{
    QLibrary mylib("Core.dll");
    mylib.load();
     //加載動態庫
     if (!mylib.isLoaded())
     {
         qDebug()<<QString("Load Oracle oci.dll failed!\n");
         return ;
     }
    p_gen_chain_word get_chain_word=(p_gen_chain_word)mylib.resolve("gen_chain_word");
    p_gen_chain_char get_chain_char=(p_gen_chain_char)mylib.resolve("gen_chain_char");
    char head = '\0',end = '\0';
    char* words[10000];
    char* result[10000];
    bool b_w = this->checkBox->isChecked()==true;
    bool b_c = this->checkBox_2->isChecked()==true;
    bool b_h = this->checkBox_3->isChecked()==true;
    bool b_t = this->checkBox_4->isChecked()==true;
    bool b_r = this->checkBox_5->isChecked()==true;
    if(b_w && b_c)
    {
        QMessageBox:: information(NULL,"Error","can't choose w and c at the same time");
        return;
    }
    if(!b_w && !b_c)
    {
        QMessageBox:: information(NULL,"Error","need a w or cs");
        return;
    }
    if(b_h)
    {
        QByteArray head_text = this->textEdit->text().toLower().toLatin1();
        head = head_text.data()[0];
        if(!(head>='a' && head <='z'))
        {
            QMessageBox:: information(NULL,"Error","head must in a-z");
            return;
        }

    }
    if(b_t)
    {
        QByteArray head_text = this->textEdit_2->text().toLower().toLatin1();
        end = head_text.data()[0];
        if(!(head>='a' && head <='z'))
        {
            QMessageBox:: information(NULL,"Error","tail must in a-z");
            return;
        }

    }

    QStringList text = this->textBrowser_2->toPlainText().split("\n");
    int len = 0;
    int word_num = String2Char(text,words);
    if(b_w)
    {
        len = get_chain_word(words,word_num,result,head,end,b_r);
    }
    if(b_c)
    {
        len = get_chain_char(words,word_num,result,head,end,b_r);
    }
    if(len<=1)
    {
        if(len == -4)
        {
            QMessageBox:: information(NULL,"Error","shouldn't have circles");
            return;
        }
        if(len == -5)
        {
            QMessageBox:: information(NULL,"Error","Chain don't exist");
            return;
        }
        QMessageBox:: information(NULL,"Error","unkown error! please input valid data");
        return;
    }
    qDebug()<<len;
    int i;
    this->textBrowser->clear();
    for(i = 0;i<len;i++)
    {
        string a(result[i]);
        this->textBrowser->append(QString::fromStdString(a));
        qDebug()<<QString::fromStdString(a);
    }
}

界面模塊與計算模塊對接

  筆者小組同其餘小組(16061161-15231112組和16061173-16061135組)進行了計算模塊dll的交換。因爲兩個小組在項目開始前就對接口部分進行了討論,在接口各參數、返回值以及功能上達成了共識,即計算接口設計部分所述。因爲完成全部模塊的設計及dll打包後,筆者在本小組的測試程序以及GUI上對dll進行了導入測試,在進入與其餘小組對接階段時並未遇到嚴重BUG,但在這過程當中也遇到了一些小問題。

  • 打包dll和GUI模塊所支持處理器不一樣。
      在最初打包dll時未注意X86和X64以及Debug和Release版本間的區別,打包出錯誤版本的dll,致使GUI沒法導入dll。在複覈了版本後就解決了這一問題。

  • 異常處理問題。
      筆者小組採用Qt Creater進行GUI部分的開發,然後端進行Visual Studio進行開發。前者對於dll拋出異常的支持較差,容易致使程序出錯。在採用返回值返回錯誤信息的方式後解決了這一問題。

  • 附對接後運行效果


開發過程總覽

  在結對以前,筆者和結對夥伴就已經認識,但從未在程序開發上進行合做。本次結對編程應當是小組兩人第一次合做開發。如下是按照項目開發的里程碑來總覽筆者小組開發的歷程。

  • 啓動題目
      在結對題目剛出來時,咱們很快就在一塊兒對需求進行了討論,很快就列出了最初的項目設計以及日程計劃,開始了項目的推動。因爲其餘課程以及實驗室任務等緣由,咱們並無不少公共的時間進行結對開發,僅有天天晚上用大約6小時左右的時間一同編程。

  • 完成第一版
      在項目啓動後約兩天,咱們就按照最初的設計(設計見性能改進部分)實現了程序的基本功能。在實現的過程當中,筆者按照單元測試的編寫要求,編寫並進行了單元測試,完成了初版的測試集,供後期迴歸測試使用。

  • 優化算法
      因爲最初的設計的時間複雜度較高,咱們在完成第一版的測試後便進入了算法的改進和優化的階段。筆者將部分的功能改成使用動態規劃算法實現,並根據搜索單詞鏈的要求設計了不一樣的遞推式。筆者的搭檔將第一版的深度搜索進行了優化以補充動態規劃未涉及的功能。通過了約4天,咱們基本完成了算法的優化,開始了新測試的編寫以及進行迴歸測試。

  • GUI開發和核心代碼封裝
      在算法優化的後半階段,咱們開始了GUI部分的同步開發以及計算模塊的封裝。這一部分是咱們整個開發過程當中最爲艱難的階段。咱們在Qt環境的配置、dll的打包及使用上遇到了不少困難。主要在於dll相似於黑盒,咱們在測試其是否打包成功並加載入Qt是耗費了不少時間。

  • 終版完成
      最終通過了兩週的結對編程,筆者和搭檔較好的完成了最長單詞鏈的程序。在這過程當中,因爲是第一次結對編程前期的效率並不高。直到進入了優化階段,咱們在逐漸找到告終對編程的節奏,最終使用了約2000分鐘完成了項目。

  • 附結對編程圖片


總結

結對編程優缺點

  通過長達兩週的結對編程,結合《構建之法》中對結對編程的描述以及這兩週的親身經歷,筆者對於結對編程的優缺點有了如下的感想。

  • 兩人合做,更易解決問題
      在編程的過程當中,筆者兩人一同遇到了許多的困難:從編程語言的使用,到算法的設計,再到代碼的實現。相較於單人編程,兩人同時對問題進行思考,經過討論發現兩人解決問題的漏洞,互補雙方解決問題的方法,有效地解決問題。好比,在筆者同夥伴一塊兒編寫單詞鏈搜索函數時對於每一個代碼分支,遞歸調用都進行了相似一人推演一人審覈的模式,有效地突破了程序中的難點。

  • 相互學習,共同進步
      在進行結對編程時,每人都有一段時間負責在對方編程的過程當中在旁邊進行審覈。在「旁審」隊友編程的過程當中,筆者從隊友的編程方式吸收了許多有用的部分,好比算法實現的思路,代碼編寫習慣等。將這些優勢融入到本身的編程中,優化了本身的編程方式。

  • 同時編程,下降溝通難度
      結對編程要求兩人同時在一塊兒進行開發。筆者與隊友在大多數時間裏是按照結對編程的要求一同編程。很明顯能感受到,本身很熟悉在這段時間的編寫的代碼,即便是對方在掌控鍵盤編寫。在必定程度上,減小了兩人閱讀對方代碼,詢問如何使用對方代碼時消耗的時間。相較之下,程序有一部分代碼並非兩人一塊兒編寫的,在使用僅由對方編寫的代碼時常會產生許多的疑惑,須要在溝通上花費很多的時間。

  • 時間規劃上存在難度
      結對開發的兩人並非一直可以抽出時間一同編程。時間的碎片化也增長了兩人一同使用大塊時間進行編程的難度。就筆者兩人而言,在這兩週中基本上只有晚上有可能同時有時間結對編程。而白天經常會是一人有空而另外一人有事的狀況。於是結對編程時,對於時間上的規劃存在必定的難度。可是若是是在相似於公司的環境中進行結對編程,這個問題應當會減輕不少。

  • 工程量較大時,進度較難推動
      結對編程和分工開發就比如單線程與多線程。當任務的工做量較大,而且各模塊的開發任務能分解爲並行度較高的子任務的時候,很明顯進行分工「多線程」開發的效率會高一些。再基於上一條,在面對工做量較大的任務,結對編程頗有可能會在推動開發進度上出現困難。在筆者結對編程的前期,兩人編程的效率是比較可觀的,很快就完成了基本功能。但隨之而來的測試、優化、迴歸測試、GUI繪製等等任務使得原本時間就不太富裕的結對編程出現的進度推動上的困難。最終筆者兩人將部分任務的任務進行了分工開發,緩解結對任務的壓力。

結對編程反思

  通過了兩週的合做,筆者對本身和隊友在結對編程中各自的亮點和不足進行了一下總結。

隊員 優勢 不足
筆者 1.代碼編寫規範
2.吃苦耐勞
3.積極推進項目
解決複雜算法問題能力有待增強
隊友 1.算法能力強
2.肝帝
3.溝通能力強
常常打錯字
相關文章
相關標籤/搜索