軟件工程結對編程博客

結對編程博客

一、GitHub項目地址

項目GitHub地址前端

二、PSP表格

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

三、看教科書和其它資料中關於Information Hiding, Interface Design, Loose Coupling的章節,說明大家在結對編程中是如何利用這些方法對接口進行設計的

  • Information Hiding:信息的隱藏和封裝,其中信息的隱藏是目的,而封裝是方法,這一行爲是爲了保證接口的安全性,避免在他人使用接口時修改了意料以外的數據,而出現問題。在結對編程中,類屬性、方法等儘可能定義爲私有,只有其餘類須要調用的方法才設置爲公有。
  • Interface Design:接口設計。接口做爲不一樣模塊之間相鏈接的部分,其設計的合理性與安全性直接關係到不一樣模塊接合時的難易度與安全性。好的接口設計應符合諸多原則如接口隔離原則、迪米特法則、開閉原則等,其目的都是爲了保證接口的安全性與實用性。結對編程中,經過事前規定、參照做業要求等,接口設計基本合理。
  • Loose Coupling:鬆耦合,簡單地說就是下降功能、函數之間的依賴程度,即一個方法儘可能只完成一個功能,方面修改,避免出現一個函數修改,全部函數都須要再次修改,這樣不免致使每次修改時工做量變大,且正確性存疑。可在接下來的模塊中看到,結對編程中儘可能符合了該要求。

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

計算模塊主要有6個類,Core類DFS類Error類Input類PreProcess類RingDfs類ios

Input類用於從輸入中提取所須要的信息,如模式、開頭結尾、路徑等。git

PreProcess類用於處理數據,包括分割單詞,並依據分割出的單詞生成圖,輸出結果等。github

DFS類用於尋找圖中是否有環。算法

RingDfs類用於根據生成的圖進行搜索,尋找符合要求的最長路徑。編程

Core類用於封裝。後端

Error類用於異常時報錯。安全

Input類

方法名 做用 調用方法
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類

方法名 做用 調用方法
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 ary) 根據輸入的容器,將其中單詞按順序輸出至solution.txt
void VecToStr(char* result[], vector ans) 將圖轉化爲單詞表格式,以知足同接口要求 Error(string str)方法

DFS類

方法名 做用 調用方法
DFS(PreProcess pp) 生成實例
void getGraph() 獲取已生成好的圖
bool hasRing() 判斷圖中是否有除了自環之外的環

RingDFS類

方法名 做用 調用方法
RingDFS(PreProcess pp) 生成實例,pp中含有圖等信息
vector initDFS(int mode, int head, int tail, int ring) 根據輸入的各種參數,以及獲取到的圖,搜索符合要求的單詞鏈,並返回結果 本地私有方法,Error(string str)方法

Core類

方法名 做用 調用方法
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) 根據輸入的單詞表、長度、參數等,搜索字母數量最多的符合要求的單詞鏈 同上

Error類

方法名 做用 調用方法
void Error(std::string str) 根據輸入的報錯信息拋出異常

算法基於bfs的改進(改進原理見第6部分),基本原理簡單易懂。對於無環的狀況下,建圖時合併了同首尾單詞,只保留字母數量最大的單詞。數據結構

五、畫出UML圖顯示計算模塊部分各個實體之間的關係

六、計算模塊接口部分的性能改進。 記錄在改進計算模塊性能上所花費的時間,描述你改進的思路,並展現一張性能分析圖(由VS 2015/2017的性能分析工具自動生成),並展現你程序中消耗最大的函數

如下爲無環,單詞數量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)中有哪些節點,所以可能會致使重複走同一條邊,該算法沒法使用。

七、看Design by Contract, Code Contract的內容 ,描述這些作法的優缺點, 說明你是如何把它們融入結對做業中的

契約式設計的優勢是,義務、責任分工明確,各自負責各自部分的正確性,簡化了開發流程,明確了各自任務。契約的存在保障了雙方的利益,對需求者來講,不須要擔憂所需部分的正確性,只須要按照契約使用便可。對於供應者來講,不須要擔憂提供的模塊如何使用等, 只須要按照契約完成模塊並確保正確性便可。

在結對編程中,咱們會規定不一樣模塊之間的接口,在發現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以及算法的正確性。

1三、看教科書和其它參考書,網站中關於結對編程的章節, 明結對編程的優勢和缺點。結對的每個人的優勢和缺點在哪裏

正如《構建之法》中所述

既然代碼複審能發現這麼多問題,有這麼好的效 果,若是咱們每時每刻都處在代碼複審的狀態, 那不是很好麼?

結對編程是將代碼複審發揮到極致後的形式,也就是說結對編程徹底繼承了代碼複審的全部優勢:加強了代碼的規範性,減小了bug出現的可能性,提升了代碼的可維護性等等。在合理的使用方法下,結對編程的效率甚至能超過並行開發的效率,考慮到測試、維護等等問題的時間成本。可是,這樣的「極限」形式也有必定的侷限,即對於程序中很是簡單,邏輯複雜度極地的部分,進行實時的代碼複審就有了「殺雞用牛刀」的意味,在這些部分上,結對編程顯然有矯枉過正的嫌疑,其開發效率在程序的簡單部分中顯然沒有並行開發高。

結對中個人優缺點:

優勢:承擔任務主動,投入時間長、任務完成的較爲完整,進行了許多優化。

缺點:與同伴溝通不足。

個人同伴的優缺點:

優勢:配合良好,完成任務優秀、負責,測試完整。

缺點:沒有及時反饋進度與問題。

1四、PSP表格

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+

1五、鬆耦合

咱們與學號1606109三、16061155的隊伍進行了鬆耦合測試,結果正確,對方使用我方Core.dll的運行結果截圖以下:

在剛開始耦合時出現了問題,由於在對於head的定義中,咱們組的邏輯是當head不爲0即認爲是規定的開頭字母,但接口內部沒有檢查其正確性,而對方認爲head不規定時爲'0'
出現問題後,咱們組在接口內部加2上了對於參數的合法性判斷,以後沒有其餘問題。

以後咱們還與1606116七、16061170組進行了耦合,運行正確,沒有問題。

相關文章
相關標籤/搜索