1.相關資訊
Q | A |
---|---|
這個作業屬于哪個課程 | 2020春季計算機學院軟體工程(羅傑 任健) |
這個作業的要求在哪裡 | 結對項目作業 |
我在這個課程的目标是 | 系統地學習軟體工程開發知識,掌握相關流程和技術,提升工程化開發的能力 |
這個作業在哪個具體方面幫助我實作目标 | 了解熟悉結對開發流程,為之後的組隊項目做好鋪墊 |
教學班級 | 005 |
項目位址 | https://github.com/NoSameRain/BUAA_SE_IntersectPrj_Pair |
2.PSP表格:
PSP2.1 | Personal Software Process Stages | 預估耗時(分鐘) | 實際耗時(分鐘) |
---|---|---|---|
Planning | 計劃 | ||
· Estimate | · 估計這個任務需要多少時間 | 20 | |
Development | 開發 | ||
· Analysis | · 需求分析 (包括學習新技術) | 200 | 300 |
· Design Spec | · 生成設計文檔 | 60 | |
· Design Review | · 設計複審 (和同僚稽核設計文檔) | ||
· Coding Standard | · 代碼規範 (為目前的開發制定合适的規範) | 30 | 40 |
· Design | · 具體設計 | ||
· Coding | · 具體編碼 | 360 | |
· Code Review | · 代碼複審 | 150 | |
· Test | · 測試(自我測試,修改代碼,送出修改) | ||
Reporting | 報告 | ||
· Test Report | · 測試報告 | 120 | |
· Size Measurement | · 計算工作量 | ||
· Postmortem & Process Improvement Plan | · 事後總結, 并提出過程改進計劃 | 90 | |
合計 | 1370 | 1720 |
3.看教科書和其它資料中關于 Information Hiding,Interface Design,Loose Coupling 的章節,說明你們在結對程式設計中是如何利用這些方法對接口進行設計的。
- Information Hiding:資訊隐藏是使用程式設計語言功能(如私有變量)或顯式導出政策來防止類或軟體元件的某些方面對其用戶端可通路的能力。例如,可以隐藏産生給定結果的計算。它遵循可以描述為一種資訊隐藏類型的功能模型。資訊隐藏的一個優點是具有靈活性,例如允許程式員更容易地修改程式。也可以通過将源代碼放置在子產品中以在将來随着程式的發展和發展輕松通路而完成。
- Interface Design:這裡的接口應該主要指的是API,即Application Programming Interface(應用程式接口),它是一些預先定義的函數,或指軟體系統不同組成部分銜接的約定。 目的是提供應用程式與開發人員基于某軟體或硬體得以通路一組例程的能力,而又無需通路原碼,或了解内部工作機制的細節。
- Loose Coupling:在程式設計中,耦合是指一個元件具有另一元件的直接知識程度,也就是軟體子產品之間的互相依賴程度。耦合通常與内聚形成對比。低耦合通常與高内聚相關,反之亦然。低耦合通常是軟體結構良好和設計良好的标志,并且與高内聚性結合使用時,可以達到較高的可讀性和可維護性的總體目标。耦合強弱取決于子產品間接口的複雜程度、進入或通路一個子產品的點以及通過接口的資料。
- 在這次結對程式設計中,我們将計算和錯誤處理這兩種功能子產品進行了封裝,在實作UI部分的時候,由于隻需要調用相應的函數,而不需要知道函數是如何實作的,就可以展現出資訊隐藏這一特征。但是前提是被封裝子產品一定要保證正确性,否則在之後調用的過程中再去修改就會帶來很多麻煩。由于一開始就确定了之後要實作的功能,是以接口的設計也就應運而生。像UI子產品中的增加幾何對象就要調用計算子產品的addline()來進行操作。是以,接口的設計應該是根據功能來定義的。這也能一定程度保證資料的安全性。在耦合度上,我們的思路是讓每一個子產品都隻實作特定的功能,各做各的事情,比如計算子產品負責計算交點,那UI子產品就不能再做相關的工作,保持每個子產品的獨立性。
4.計算子產品接口的設計與實作過程。設計包括代碼如何組織,比如會有幾個類,幾個函數,他們之間關系如何,關鍵函數是否需要畫出流程圖?說明你的算法的關鍵(不必列出源代碼),以及獨到之處。
本次作業裡有三個類(Point,Line,WFLine),Line是存儲計算交點所需的直接資訊,WFLine是為處理有無窮多交點即Line重合設計的類,包含3個參數$a,b,c$,即采用$ax+by+c=0$的直線表達式;$a,b,c$是由兩個點的坐标計算而來,故WFLine繼承自Point。關于Point和Line,WFLine的函數聲明都放置在assis.h中。
需要實作的重要函數有以下:1.确定直線參數;2.求直線交點;3.判斷交點是否線上段或者射線上;4.錯誤輸入處理;5.判斷是否有無窮多交點;6.set用于去重的運算符重載函數;
我們設計的接口如下:
DLL_API void readin(string FileName);//讀入資料
DLL_API void clear();//清空所有容器
DLL_API void cnt_coor_num(); //計算交點個數
DLL_API set<Point> getPoints();//傳回交點集合
DLL_API vector<line> getLine();//傳回直線集合
DLL_API void addLine(line l);//添加直線
DLL_API void delLine(line l);//删除直線
DLL_API void solve();//求解
DLL_API void write(string FileName);//寫入檔案
DLL_API string inputHandler(string FileName);
//從檔案讀入資料時進行錯誤處理 正常應傳回 "Everything is ok!"
DLL_API string InfinitePoints();
//判斷線之間是否重合 正常應傳回"NO InfinitePoints!"
DLL_API int getP_cnt();//獲得交點個數
DLL_API int getPointNum();//points size
程式開始時,各函數調用順序如下:

在addLine之後需要調用InfinitePoints()以判斷是否有無窮多交點,再調用交點計算子產品進行計算。
5.閱讀有關 UML 的内容:UML。畫出 UML 圖顯示計算子產品部分各個實體之間的關系(畫一個圖即可)。
由于此次作業中類與類之間的關系未做過多設計,故在starUML中畫出的圖比較簡略:
6.計算子產品接口部分的性能改進。記錄在改進計算子產品性能上所花費的時間,描述你改進的思路,并展示一張性能分析圖(由VS 2015/2017的性能分析工具自動生成),并展示你程式中消耗最大的函數。
性能改進:40min.
主要的性能改進是在存儲交點方面,一開始我們采用的是将節點的Xpoint和Ypoint轉化為string以達到去重的目的,并存儲在map容器中,經過性能分析發現to_string和insert函數開銷特别大,是以換成了直接存儲結點對象(含有兩個double屬性)到set容器中,同樣可以達到去重的目的。改進前性能分析圖如下:
可以看出to_string函數和map容器的insert函數開銷極大。改進後:
主要的CPU開銷是在set的insert操作。
7.看 Design by Contract,Code Contract 的内容:描述這些做法的優缺點,說明你是如何把它們融入結對作業中的。
- 優點:降低錯誤率,便于明晰代碼設計思路,每個人都配置設定好自己的任務,各司其職,更提高整體工作效率
- 缺點:真正實行起來有一定難度,有可能适得其反
- 這次作業中,兩個人都按照一定“契約”進行任務,比如兩人各自開發要實作的子產品,然後進行對接,同時也負責對對方子產品進行測試的工作,既有執行契約,又有核查契約。
8.計算子產品部分單元測試展示。展示出項目部分單元測試代碼,并說明測試的函數,構造測試資料的思路。并将單元測試得到的測試覆寫率截圖,發表在部落格中。要求總體覆寫率到 90% 以上,否則單元測試部分視作無效。
本次單元測試考慮了一下幾個部分的内容:
- 圖形類型L/R/S:将L-L,L-R,L-S,R-R,R-S,R-L,S-S,S-R,S-S這幾種情況組合形成更複雜的情況。因為本次作業新增了射線和線段,是以其中要特别考慮其延長線相交但交點不在射線/線段上的情況。
TEST_METHOD(testMethod16) { //S-S-R clear(); N = 3; line l1, l2, l3; l1.store_coor("S 1 2 4 5"); l2.store_coor("S 2 5 3 1"); l3.store_coor("R 1 0 5 2"); coor_4_line.push_back(l1); coor_4_line.push_back(l2); coor_4_line.push_back(l3); cnt_coor_num(); Assert::AreEqual(intersection.size(), (size_t)2); } TEST_METHOD(testMethod22) {//S-S 其中一個垂直且假交點在垂直延伸線上的 clear(); N = 2; line l1; line l2; l1.store_coor("S 1 0 0 1"); l2.store_coor("S 1 1 1 2"); coor_4_line.push_back(l1); coor_4_line.push_back(l2); cnt_coor_num(); Assert::AreEqual(intersection.size(), (size_t)0); }
- 圖形之間的位置關系:考慮L、R、S兩兩平行、相交的情況。從斜率角度出發,考慮任意兩個圖形,一個有斜率另一個斜率不存在,兩個斜率都存在(斜率相等、不相等),兩個斜率都不存在的情況以及上述情況的組合。
TEST_METHOD(testMethod21) { //有斜率且斜率相同端點相交的S-S clear(); N = 2; line l1; line l2; l1.store_coor("S 0 0 1 1"); l2.store_coor("S 1 1 2 2"); coor_4_line.push_back(l1); coor_4_line.push_back(l2); cnt_coor_num(); Assert::AreEqual(intersection.size(), (size_t)1); }
- 邊界條件:如坐标點存在100000,-100000的情況
TEST_METHOD(testMethod15) { clear(); N = 3; line l1, l2, l3; l1.store_coor("L 100000 100000 -3 19999"); l2.store_coor("L 99999 100000 4 8366"); l3.store_coor("L -6542 768 9999 -4"); coor_4_line.push_back(l1); coor_4_line.push_back(l2); coor_4_line.push_back(l3); cnt_coor_num(); Assert::AreEqual(intersection.size(), (size_t)3); }
- 零點正負号問題:如之前出現了将(0,0)和(0,-0)判斷為兩個點的情況,并通過單元測試進行了排查
TEST_METHOD(testMethod20) { clear(); N = 4; line l1, l2,l3,l4; l1.store_coor("S 0 0 -1 1"); l2.store_coor("R 1 1 2 2"); l3.store_coor("R 1 1 0 0"); l4.store_coor("R 0 0 1 -1"); coor_4_line.push_back(l1); coor_4_line.push_back(l2); coor_4_line.push_back(l3); coor_4_line.push_back(l4); cnt_coor_num(); Assert::AreEqual(intersection.size(), (size_t)2); }
- 精度問題 : 讓任意兩個,隻在小數點後十位或某一位數值上有差别的點來作為交點,并設計出相應的幾何圖形,測試這兩個交點有沒有被判定為同一個交點。但我們在這裡實作的并不是很好,是以未展示出.之後會繼續學習其他同學的部落格來進行改正.
- 将以上情況加以集合并以此作為覆寫率測試的依據,最後得到覆寫率以及測試結果如下:
9.計算子產品部分異常處理說明。在部落格中詳細介紹每種異常的設計目标。每種異常都要選擇一個單元測試樣例釋出在部落格中,并指明錯誤對應的場景。
錯誤類型 | 輸出 | 測試樣例 |
---|---|---|
類型不符合L S R | Please input line type as "L","S","R" at line | 2 K 1 2 4 5 L a 0 0 1 1 |
‘-’後未緊跟數字 | Make sure ‘-’ is followed by number.Error at line | 3 L 0 0 1 1 R 3 2 0 5 S 9 - 7 8 |
坐标範圍超限 | Make sure that the range of points is(-100000,100000).Error at line | L 1000001 4 5 1 |
line中兩點重合 | Please input two different points.Error at line | L 1 4 5 1 R 3 2 0 3 S 7 8 7 8 |
輸入資料不完整 | TOO SHORT!Please input as "L\S\R int_x1 int_y1 int_x2 int_y2" for each line.at line | 4 L 6 4 3 2 S 9 7 5 |
輸入過多元素 | TOO LONG!Please input as "L\S\R int_x1 int_y1 int_x2 int_y2" for each line.Error at line | S 9 5 6 7 8 |
輸入非整數 | Please input integers.Error at line | S 9 5 6 7 R 4.45 6 7 6 |
找不到輸入檔案 | File is Not Found | input.txt移除 |
第一行不是整數 | Make sure the first line is an Integer that greater than or equal to 1. | a |
第一行小于1 | Make sure N is an integer greater than or equal to 1. | |
type和數字沒有空格 | Make sure there is a space between number and letter. | L 0 1 1 3 |
以下是一部分運作截圖:
10.界面子產品的詳細設計過程。在部落格中詳細介紹界面子產品是如何設計的,并寫一些必要的代碼說明解釋實作過程。(5')
本次界面子產品的實作用到了Qt插件.UI設計這一部分由于知識點比較繁雜,花費了非常多的時間,首先是閱讀官方文檔,對基本的類和函數有一個大緻的了解.比如信号槽機制以及一些繪圖控件.前期準備大概花了一兩天,實際寫代碼并沒有花費太久(主要還是負責封裝的同學接口寫的好,調用起來非常順暢,整體上功能的實作思路也就比較清晰)後期解決qt的各種報錯又花了很多時間,主要還是對lib,dll檔案的引用不夠了解,導緻在屬性設定上走了很多彎路.
現在回過頭來看代碼部分其實思路很簡單,功能也就那幾個.接下來講一下我的設計流程以及思路.
設計UI我是從功能的角度出發的,首先明确要實作那些功能:添加幾何對象,删除幾何對象,從檔案導入幾何對象的描述,繪制幾何對象,繪制幾何對象交點,顯示交點個數,抛出異常資訊,退出界面.
大緻明确了要實作這些功能後,我首先利用Qt Designer對窗體進行了大緻的可視化設計.
Designer(界面設計器)設計 ui 界面最終轉換為 C++ 代碼(ui_類名.h),和我們寫的 Qt 代碼是大同小異的,假如我們對某些部件操作不熟悉,不知道該如何用其相應函數,這時候我們通過 Designer(界面設計器)拖拽此部件,修改其所需屬性,接着編譯,看其自動轉換的 C++ 代碼如何實作,這樣可學習其相應函數的用法.
以上步驟完成後開始利用代碼進行具體的功能設計與實作.首先在源檔案中對幾個功能按鈕進行了信号槽設定,當butoon被按下後就會觸發相應的功能,這裡我學到了一點就是,按照qt的命名方法對函數進行命名可以省去connect部分的代碼,實作button與相應功能的直接連接配接.以下為幾個主要的功能,分别對應5個button(添加對象、删除對象、繪制幾何對象、繪制交點、添加檔案)
private slots:
void on_AddLine_clicked();
void on_DelLine_clicked();
void on_GenerateGraph_clicked();
void on_GeneratePoint_clicked();
void on_AddFile_clicked();
然後是幾個具體要實作的功能.添加幾何對象和删除幾何對象部分很簡單,就是調用了intersect子產品的addline和delline接口,并且進行了對輸入的資訊異常以及輸入的幾何對象有無窮交點進行了判斷,這裡展示部分代碼,一些細節部分都使用星号略去
void intersect_ui::on_AddLine_clicked()
{
*****
QString text = ui.InputStr->text();
if (text.size() == 0)
{
ui.error_messege->setText("please input text");
}
else
{
*****
addLine(l1);//添加直線
*****
if (message1 != "NO InfinitePoints!")
{
ui.error_messege->setText(qmessage1);
delLine(l1);//删除直線
}
}
ui.InputStr->clear();
}
void intersect_ui::on_DelLine_clicked()
{
*****
QString text = ui.InputStr->text();
if (text.size() == 0)
{
ui.error_messege->setText("please input text");
}
else
{
*****
delLine(l1);//删除直線
}
ui.InputStr->clear();
}
繪制幾何對象部分花費了比較長的時間.這裡用到了QT-Qcustomplot來實作基礎坐标軸功能以及圖像的繪制.其中直線,線段,射線的繪制我查閱了QCPAbstractItem官方文檔用到了QCPItemStraightLine以及QCPItemLine類來實作.這裡以繪制直線為例進行展示,射線和線段部分略去
void intersect_ui::on_GenerateGraph_clicked()
{
*****
vector<line> lines = getLine();//傳回直線集合
for (int i = 0;i < lines.size();i++)
{
if (lines[i].type == "L")
{
QCPItemStraightLine* l = new QCPItemStraightLine(ui.widget);//構造直線
ui.widget->addItem(l);//添加到圖中
l->setPen(QPen(Qt::blue)); //設定畫筆
l->point1->setCoords(lines[i].x1, lines[i].y1);
l->point2->setCoords(lines[i].x2, lines[i].y2);
ui.widget->replot();
}
else if (lines[i].type == "R")
{
*****
}
else if (lines[i].type == "S")
{
*****
}
}
//設定可拖拽 滾輪放大縮小 圖像可選擇
ui.widget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
}
通過查閱資料我在坐标軸中實作了拖拽,滾輪放大縮小功能,便于檢視生成的幾何對象.
生成交點與這裡大同小異,就是調用了intersect子產品中的傳回交點坐标接口然後根據坐标點資訊進行繪制,這裡不再詳述.
輸入檔案部分調用核心子產品中的讀檔案接口和異常處理接口,實作也非常簡單.
void intersect_ui::on_AddFile_clicked()
{
QString text = ui.InputFileName->toPlainText();
string str = text.toStdString();
string message = inputHandler(str);
QString qmessage = QString::fromStdString(message);
if (message == "Everything is ok!")
{
string message1 = InfinitePoints();
QString qmessage1 = QString::fromStdString(message1);
if (message1 == "NO InfinitePoints!")
{
readin(str);//讀入資料
}
else
{
ui.error_messege->setText(qmessage1);
}
}
else
{
ui.error_messege->setText(qmessage);
}
11.界面子產品與計算子產品的對接。詳細地描述 UI 子產品的設計與兩個子產品的對接,并在部落格中截圖實作的功能。(4')
子產品對接其實我在上一點中也已經提到了.我們的思路很簡單,實作什麼功能的槽函數就對接實作什麼功能的計算子產品的接口.
舉例來講,就是UI子產品的以下函數
private slots:
void on_AddLine_clicked(); //添加幾何對象
void on_DelLine_clicked(); //删除幾何對象
void on_GenerateGraph_clicked(); //繪制幾何對象圖形
void on_GeneratePoint_clicked(); //繪制交點
void on_AddFile_clicked(); //讀檔案
分别對應調用核心子產品中以下函數
DLL_API void addLine(line l);//添加幾何對象
DLL_API void delLine(line l);//删除幾何對象
DLL_API vector<line> getLine();//傳回幾何對象集合
DLL_API set<Point> getPoints();//傳回交點
DLL_API void readin(string FileName);//從檔案讀入資料
同時考慮到錯誤處理,還調用了用于檢查檔案格式,判斷是否有無窮交點等的函數.具體怎麼調用,在什麼時候調用,上面的代碼部分都進行了展示.從總體的邏輯上來看就是以下三步:
- 槽函數響應事件
- UI子產品中相應函數被執行
- UI子產品中函數調用核心子產品中函數來實作特定功能并更新核心子產品資料
功能展示截圖如下:
- 繪制幾何對象及交點,顯示交點個數,縮放大小
- 顯示異常資訊(這裡展示了輸入為空、找不到該檔案、某兩個幾何對象有無窮交點的錯誤資訊)
12.描述結對的過程,提供兩人在讨論的結對圖像資料(比如 Live Share 的截圖)。關于如何遠端進行結對參見作業最後的注意事項。(1')
遠端結對感覺還是挺有挑戰的,在作業的第一個星期因為我和隊友都有馮如杯需要準備還有别的科目任務,是以交流比較少,大家是分頭行動,比如增加功能和測試,各司其職;在第二個周開始共享螢幕,視訊交流,效率還是提升很多,對接口的設計和使用也更為快捷。
這次項目我和隊友的結對分工是,我負責step1擴充功能和step4UI子產品,隊友負責step2封裝和step3錯誤處理。并且每個人寫好各自的部分後都要交給對方測試與檢查。是以整個任務的配置設定并不是完全割裂開來的,彼此都要對對方的任務做到心裡有數,要大緻浏覽對方的代碼。
我非常慶幸的是隊友的目标很明确,沒有僅僅就一個子產品埋頭寫代碼,比如她在封裝的時候就會非常體貼地考慮UI要怎麼和其他子產品對接,這為我之後的工作帶來了非常大的便利。
雖然留下了些小遺憾,但總體而言,兩人結對的過程是非常寶貴且有意義的,我從這次項目學到了非常多東西(不僅限于程式設計上的知識,還有如何與人合作)當然也和隊友結下很深厚的友誼。
螢幕共享截圖:
13.看教科書和其它參考書,網站中關于結對程式設計的章節,例如:http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html ,說明結對程式設計的優點和缺點。同時描述結對的每一個人的優點和缺點在哪裡(要列出至少三個優點和一個缺點)。(5')
優點:
- 兩人在一起程式設計,更能集中讨論問題,發現問題,提高效率
- 一人設計一人稽核,使得代碼品質更高,減少錯誤率
- 兩人在一起互相鼓勵,更有動力
- 二人程式設計更利于發現對方代碼中的漏洞,和一些盲點
缺點:
- 兩人程式設計習慣不同可能導緻需要很多時間适應對方風格、閱讀彼此代碼也會有些困難
- 兩人在前期沒有發現系統版本不比對,而各幹各的,到後期對接時會出現很多問題
我的優點與缺點:
- 優:心态穩如老狗,遇到Bug會一直de下去
- 優:不抛棄、不放棄,在遇到瓶頸的時候安慰鼓勵隊友
- 優:查找資料能力比較好
- 缺:前期懈怠後期玩命
隊友的優點與缺點:
- 優:push主力,一直帶動我推進(謝謝隊友)我找bug找到半夜還開着語音陪我
- 優:認真、負責,每一個任務都很用心的在做好
- 優:細心,經常找到一些我發現不了的bug
- 優:人美,聲音好聽
- 缺:遇到瓶頸的時候有點小情緒,不過很可愛
14.在你實作完程式之後,在附錄提供的PSP表格記錄下你在程式的各個子產品上實際花費的時間。(0.5')
見上。