軟工_第一次結對編程做業博客

項目 內容
此做業屬於北航軟件工程課程 班級博客連接
做業要求見右方連接 做業要求
我在這門課程的目標是 培養專業的軟件開發能力
這個做業在哪一個具體方面幫助我實現目標 對結對編程有了必定的實踐經驗,對合做開放項目的大體步驟有了必定了解和實踐;
本次做業GitHub項目地址 https://github.com/NanonaN/WordChain

一、github 項目地址:見上表

二、PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃
· Estimate · 估計這個任務須要多少時間 30
Development 開發
· Analysis · 需求分析 (包括學習新技術) 120
· Design Spec · 生成設計文檔 30
· Design Review · 設計複審 (和同事審覈設計文檔) 30
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 30
· Design · 具體設計 100
· Coding · 具體編碼 420
· Code Review · 代碼複審 90
· Test · 測試(自我測試,修改代碼,提交修改) 240
Reporting 報告
· Test Report · 測試報告 50
· Size Measurement · 計算工做量 30
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 90
合計 1260

三、淺談Information Hiding, Interface Design, Loose Coupling

3.一、Information Hiding在程序中的體現

​ Information Hiding即信息隱藏,很容易理解,就是把一個類中重要銘感的信息隱藏在自身內部,類以外的代碼沒法獲取並更改這些信息,以此來提升程序的安全性;git

​ 在這次的項目實現中,由於程序實現難度並不大(若不是對性能有極致的要求),因此計算模塊僅一個類——Core,該類中有八個屬性(下一部分中有介紹),分別存儲計算單詞鏈相應的信息,這些信息對於不一樣的功能需求是不一樣的,可是如果直接生成get,set方法甚至將屬性改成public屬性,這將大大下降程序的安全性,也是破壞了Information Hiding的原則;github

​ 因此對於每次調用程序的信息,咱們採用了先存儲在構造Core實例的方法,好比GUI界面下,用戶能夠實時改變功能選項,這時程序所作的事只是存儲選項的改變,當用戶點擊「開始計算」時,才構造core實例(傳入以前存儲的信息)進行計算 ;算法

3.二、Interface Design和Loose Coupling在程序中的體現

​ Interface Design和Loose Coupling在我看來是結合一體的吧,接口設計實際上就是實現鬆耦合(我的觀點);編程

以前看過一個博客中的例子簡明的指出了二者的好處:設計模式

在作一個團隊項目時,有人可能負責領域模型m(model),有人負責前臺v(view),有人負責業務邏輯c(controller),在這種mvc的設計模式模式驅動下,咱們首先想到的就是:定義一套公共的接口,方便各個模塊之間的通信。面向接口的程序設計思想是一種有效的手段。好比一個作業務邏輯的team,他們並不清楚業務數據的curd實現,但他們只要經過面向於數據組提供的一整套接口進行編程便可,同時,數據組的開發能夠並行進行,這樣,不須要等待一個模塊的完成就能夠預先「使用"這個模塊,極大的提升了團隊的效率。安全

​ 在該程序實現中,程序的主體就分爲三個部分,第一是處理輸入,第二是進行計算,第三是輸出(這裏也包括GUI的調用);mvc

  • 處理輸入部分接收字符串或者功能選項(GUI中如此),而後返回一個存儲全部單詞的List;
  • 計算部分則是傳入一個單詞序列(即list),返回最長單詞鏈;
  • 輸出部分即獲取最長單詞鏈進行輸出,固然在GUI中還須要判斷輸出的方式;

三個部分之間的鏈接簡單明瞭,相互之間沒有什麼衝突,故不須要等一個模塊完成在進行下一個模塊,而是並行編碼;app

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

程序計算模塊就一個類:core類;ide

  • 類屬性如圖:

其中,charMode和wordMode分別表明(-c和-w選項),head和tail表明限定的首尾單詞,input表明輸入的字符串,inputIsFile表明文本輸入仍是文件輸入(GUI中須要此選項);模塊化

  • 類方法如圖:

  • 計算單詞鏈的核心流程爲:

​ main函數構造core類實例,構造函數中經過調用ParseCommandLineArguments()函數將命令函輸出的字符串分割並提取出其中的信息;

​ 而後交由GenerateChain()函數進行計算,ReadContentFromFile()爲讀取字符串,DivideWord()函數爲將字符串分割爲單詞,FindLongestChain()函數爲計算最長單詞鏈。

  • 其中核心算法函數爲FindLongestChain函數,該函數經過遞歸的方法求得其中的單詞鏈並取其中(單詞或者字母)最長的一條單詞鏈返回;

五、UML圖

六、計算模塊的性能改進

​ 這一部分花費了咱們大約三個小時吧,該問題能夠等價爲尋找有向有環圖中最長路徑的問題,是一個NPC問題,因此對於容許單詞環的狀況下,算法複雜度沒法在數量級層面進行優化,故對一些細節進行優化,好比訪問尋找單詞的效率,無-r選項時及時退出;核心算法上,沒有作根本上的優化,採起BFS算法遍歷找出最長的單詞鏈。

​ VS性能分析工具分析結果如圖:

七、淺談Design by Contract, Code Contract

按合同設計相似於以前OO課中編寫JSF的規格化設計思路,個人理解實際上就是上文中接口設計的細化和量化,即對於整個程序編碼,細化到每個函數,都進行量化的規範設計,如連接中的前置條件、後置條件等規範;

這樣作的好處是:

  • 比較嚴格的遵循了先設計在編寫的原則,編寫代碼時井井有理不容易出錯;
  • 各個方法功能與屬性很是明確,耦合度低;
  • 有利於測試和發現程序BUG;
  • 這樣的合同設計算是設計文檔的進一步細化,更有利於團隊合做開發;

缺點是:

  • 略顯得固化死板,如果整個程序即每個方法都遵循這樣的設計思路,可能對於一些及其簡單的函數反而增長了無用的工做量。

八、單元測試

8.一、構造測試數據的思路

​ 核心思路就是儘量覆蓋全部的狀況,即包括正確和錯誤的輸入樣例,正確的輸入樣例分別測試該程序支持的各類功能,錯誤的輸入樣例則測試程序在遇到不合法輸入時可否正確報錯和退出;

8.二、單元測試部分代碼
  • 單詞鏈輸入文本樣例:
private readonly List<string> _wordList1 = new List<string>()
  {
      "Element",
      "Heaven",
      "Table",
      "Teach",
      "Talk"
  };

  private readonly List<string> _wordChain1WithR = new List<string>()
  {
      "table",
      "element",
      "teach",
      "heaven"
  };

  private readonly List<string> _wordList2 = new List<string>()
  {
      "Algebra",
      "Apple",
      "Zoo",
      "Elephant",
      "Under",
      "Fox",
      "Dog",
      "Moon",
      "Leaf",
      "Trick",
      "Pseudopseudohypoparathyroidism"
  };

  private readonly List<string> _wordChain2 = new List<string>()
  {
      "algebra",
      "apple",
      "elephant",
      "trick"
  };

  private readonly List<string> _wordChain2WithC = new List<string>()
  {
      "pseudopseudohypoparathyroidism",
      "moon"
  };

  private readonly List<string> _wordChain2WithHe = new List<string>()
  {
      "elephant",
      "trick"
  };

  private readonly List<string> _wordChain2WithTt = new List<string>()
  {
      "algebra",
      "apple",
      "elephant"
  };
  • 方法測試:
public unsafe void gen_chain_wordTest()
    {
        TestGenChain(_wordList1, _wordChain1WithR, enableLoop: true);
        TestGenChain(_wordList2, _wordChain2);
        TestGenChain(_wordList2, _wordChain2WithHe, head: 'e');
        TestGenChain(_wordList2, _wordChain2WithTt, tail: 't');
    }
    [TestMethod()]
    public void gen_chain_charTest()
    {
        TestGenChain(_wordList2, _wordChain2WithC, mode: 1);
    }
    private unsafe void TestGenChain(List<string> wordList, List<string> expectedChain, int mode = 0, char head = '\0', char tail = '\0', bool enableLoop = false)
    {
        var resultArray = CreateStringArray(wordList.Count, 100);
        var wordListArray = ConvertToArray(wordList);
        var len = 0;
        switch (mode)
        {
            case 0:
                len = Core.gen_chain_word(wordListArray, wordListArray.Length, resultArray, head, tail, enableLoop);
                break;
            case 1:
                len = Core.gen_chain_char(wordListArray, wordListArray.Length, resultArray, head, tail, enableLoop);
                break;
            default:
                Assert.Fail();
                break;
        }
        var result = ConvertToList(resultArray, len);
        CollectionAssert.AreEqual(expectedChain, result);
    }
     [TestMethod()]
        public void ParseCommandLineArgumentsTest()
        {
            TestWrongArgs("-w");
            TestWrongArgs("-c");
            TestWrongArgs("-h");
            TestWrongArgs("-t");
            TestWrongArgs("-");
            TestWrongArgs("-h 1");
            TestWrongArgs("-t 233");
            TestCorrectArgs("-w input.txt");
            TestCorrectArgs("-c input.txt");
            TestCorrectArgs("-w input.txt -h a");
            TestCorrectArgs("-w input.txt -t b");
            TestWrongArgs("abcdefg");
            TestWrongArgs("-w input.txt -h 9");
            TestWrongArgs("-w input.txt -t 0");
            TestWrongArgs("-c input.txt -h 7 -t 1");
            TestWrongArgs("-w input.txt -h 123");
            TestWrongArgs("-c input.txt -t 321");
            TestWrongArgs("-h a");
            TestWrongArgs("-w input.txt -w input.txt");
            TestWrongArgs("-w input.txt -c input.txt");
            TestWrongArgs("-c input.txt -c input.txt");
            TestWrongArgs("-c input.txt -w input.txt");
            TestWrongArgs("-w input.txt -h a -h c");
            TestWrongArgs("-w input.txt -t a -t c");
            TestWrongArgs("-w input.txt -r -r");
            TestCorrectArgs("-w input.txt -r");
        }
  • 附上單元測試覆蓋率截圖

九、異常處理

程序中的異常分爲讀入參數時發生的異常和運行發現單詞環(沒有-r選項)

  • 異常類型圖:

  • 對應的程序的中測試樣例爲:

    public void ParseCommandLineArgumentsTest()
        {
            TestWrongArgs("-w");
            TestWrongArgs("-c");
            TestWrongArgs("-h");
            TestWrongArgs("-t");
            TestWrongArgs("-");
            TestWrongArgs("-h 1");
            TestWrongArgs("-t 233");
            TestWrongArgs("abcdefg");
            TestWrongArgs("-w input.txt -h 9");
            TestWrongArgs("-w input.txt -t 0");
            TestWrongArgs("-c input.txt -h 7 -t 1");
            TestWrongArgs("-w input.txt -h 123");
            TestWrongArgs("-c input.txt -t 321");
            TestWrongArgs("-h a");
            TestWrongArgs("-w input.txt -w input.txt");
            TestWrongArgs("-w input.txt -c input.txt");
            TestWrongArgs("-c input.txt -c input.txt");
            TestWrongArgs("-c input.txt -w input.txt");
            TestWrongArgs("-w input.txt -h a -h c");
            TestWrongArgs("-w input.txt -t a -t c");
            TestWrongArgs("-w input.txt -r -r");
        }

十、界面模塊設計及其與計算模塊的對接

  • GUI界面整體設計如圖:

注:計算默認爲計算單詞最多和直接輸入單詞文本,可改變選項爲文件輸入,文件輸入分爲輸入文件路徑或者點擊「選擇單詞文件」按鈕選擇指定文件;

結果默認爲輸出到當前界面,如:

亦可選擇導出到指定文件,導出完成會有彈窗提示:

若發生異常,會彈窗提示錯誤信息,如:

下面簡單介紹實現過程:

​ 在顯示的GUI界面中,每個選項框都是一個窗體控件,由checkBox控件實現可選可不選的功能選項(好比容許單詞環選項),由radioButton實現多選一的必選項(這裏須要用pannel容器框起來);由Button實現按鈕點擊(如開始計算按鈕);由textBox實現文本輸入;由label實現顯示功能;經過MessageBox.show函數實現彈窗提示;

​ 編寫各控件的事件代碼,除「開始計算單詞鏈」按鈕之外,其他事件主要功能爲將用戶選擇的信息(作相應處理)存儲起來,當用戶點擊「開始計算單詞鏈」按鈕後,程序將GUI中的信息及以前存儲的信息傳入構造函數Core()以此構造一個示例,而後調用core.GenerateChain()方法計算出最長單詞鏈,最後進行輸出。

十一、結對討論照片

十二、結對編程優缺點

12.一、結對編程的優勢
  • 能夠在彼此合做中學習對方的優勢並及時發現本身的問題;
  • 能夠鍛鍊與他人合做開發工程項目的能力;
  • 經過明確的分工能夠鍛鍊模塊化能力;
  • 能夠增進與同窗的友誼;
12.二、結對編程的缺點
  • 一部分時間將花費在討論和安排分工上;
  • 工做中可能有可能出現重合或者衝突的狀況(固然這實際上是本身的問題,須要實踐經驗吧);
12.三、結對中每一個人的優缺點
  • 個人缺點:

    在系統的工程開發中存在部分短板,好比寫單元測試不夠熟練;

    編碼規範還有待提升。

    至於優勢…仍是由隊友評價比較客觀吧hh;

  • 隊友的優勢:

    恩…有點多…受益良多,一一道來:

    編碼能力比較強,代碼規範且效率很高;

    工程能力很強,各方面能力全面;

    學習能力強且工做效率高;

    至於缺點…沒發現…(是實際感受)。

1三、PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃
· Estimate · 估計這個任務須要多少時間 30 30
Development 開發
· Analysis · 需求分析 (包括學習新技術) 120 100
· Design Spec · 生成設計文檔 30 30
· Design Review · 設計複審 (和同事審覈設計文檔) 30 30
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 30 30
· Design · 具體設計 100 60
· Coding · 具體編碼 420 480
· Code Review · 代碼複審 90 120
· Test · 測試(自我測試,修改代碼,提交修改) 240 180
Reporting 報告
· Test Report · 測試報告 50 40
· Size Measurement · 計算工做量 30 30
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 90 60
合計 1260 1250
相關文章
相關標籤/搜索