天天看點

2020軟體工程-結對項目作業

2020軟體工程-結對項目作業

1. 項目簡介

github連結

  • https://github.com/CapFreddy/BUAASE-PairPrograming
  • 教學班級:周五班 006
項目 内容
這個作業屬于哪個課程 https://edu.cnblogs.com/campus/buaa/BUAA_SE_2020_LJ
這個作業的要求在哪裡 https://edu.cnblogs.com/campus/buaa/BUAA_SE_2020_LJ/homework/10466)
我在這個課程的目标是 提升軟體開發能力
這個作業在哪個具體方面幫助我實作目标 提升了個人程式設計能力, 測試和提升代碼性能的能力

2. PSP時間花費

PSP2.1 Personal SoftWare Process Stages 預計耗時 實際耗時
Planning 計劃 120 60
Estimte 估計這個任務需要多少時間 1800 1700
Development 開發 1100 1000
Analysis 需求分析
Desing Spaec 生成設計文檔 100 40
Design Review 設計複審 10
Coding Standard 代碼規範
Design 具體設計
Coding 具體編碼 180
Code Review 代碼複審
Test 測試
Reporting 報告
Test Report 測試報告
Size Measurement 計算工作量 5
Postmortem & Process Improvement 事後總結 50
- 合計

3. 接口設計

經過查閱資料和學習, 我了解到在接口設計中有常用的一些原則, 本次作業的設計中, 我們用到了這些原則。

  • 單一職責原則
    Single Responsibility Principle: There shuld never be more than one reason for class to change
    單一職責原則要求一個接口, 一個功能, 如果在private 方法和public接口中, 改動過多, 那麼容易引起錯誤。 在結對程式設計的設計中, 計算子產品裡我們盡量保證一個函數隻做一個事情, 比如Line Circle Point分出三個類, 每個求交點的函數隻處理一種類型等。
  • 裡氏替換原則
    Liskov Substitution Principle: Functions that use pointers or references to base classed must be able to use objecsts of derived classes without knowing it
    這一法則是多态的展現, 所有父指針的使用點都可以用子指針, 這要求了子類隻擴充父類, 但是不可以用在覆寫父類函數的時候讓函數的應用範圍減少。 在最初的版本中, 我們采用了Line Circle繼承父類的方式實作, 後來發現這種繼承影響性能, 就放棄了繼承。
  • 依賴倒置原則

    依賴倒轉原則是TDD測試驅動開發的展現, 要求高層子產品不依賴特定的底層子產品, 而是依賴其抽象, 這要求程式設計者提前想好接口。

  • 接口隔離原則
    clients should not be forced to depend upon interfacess that they don't use
    保證接口單一, 不要過于臃腫, 在計算子產品和UI子產品的對接時, 我們盡量細化接口, 讓子產品的每個按鈕對應了剛好一個接口, 同時接口之間的功能達到了隔離。
  • 迪米特法則

    law of Demeter: least knowledge

    loose coupling: Loose coupling means they mostly independent

    這一原則要求兩個類之間要低耦合, 達到loose coupling, 一個類不需要知道另一個類是怎麼實作的, 更不需要因為一個類内部代碼的修改二更改自己的方法。 在我們的結對程式設計中, 為了實作接口的高效對接, 對于接口進行和抽象和封裝

4.計算子產品接口的設計與實作過程

類設定

  共設定4個類,分别為表示直線、射線、線段的

Line

類(通過成員

m_type

區分),表示圓的

Circle

類,表示交點的

Node

類,以及負責維護幾何對象、計算交點及輸出結果的主類

Intersect

類。幾何對象類的成員包含構造參數以及為求解交點時減少計算量而預存的衍生量。此外,

Line

類還包含一個判斷輸入的交點是否在該線上的

online

成員函數。

主類接口

  主類

Intersect

在執行個體化後可以通過

addGeoFromFile

函數從目标路徑檔案中讀入幾何對象,也可以通過

addGeoByString

函數由指定格式的字元串添加幾何對象。還可以通過

removeGeoByString

函數删除容器中和指定格式的字元串所表示的幾何對象相同的幾何對象。主類提供

CalculateIntersections

函數用于觸發交點的計算,這個函數通過調用相應的交點計算函數計算每兩個幾何體所形成的交點并儲存在容器中。在交點計算完畢後可以通過

GetIntersectionNumber

函數得到交點個數,或使用

GetGeoObjects

GetIntersections

函數輸出幾何對象的具體資訊,也可通過

ViewIntersections

函數列印交點至标準輸出以快速調試。

  主類在執行個體化時生成存放幾何對象及交點的容器,并在後續過程中不斷更新。UML圖如下:

2020軟體工程-結對項目作業

交點計算

  交點的計算根據代數或幾何方法實作。其中,線間交點的求解依賴行列式,較為工整規範;線圓交點的求解通過将圓心平移至原點,簡潔地計算出交點後再平移回去的方法實作(可視為坐标系的線性變換);圓圓的交點則通過相交部分的三角關系求解。具體如下:

LineLineIntersect

  對于\(L_1\{(x_1,y_1), (x_2,y_2)\},L_2\{(x_3,y_3),(x_4,y_4)\}\),交點為:

\[x=\frac{

\left|\begin{array}{}

x_1 & y_1\\

x_2 & y_2

\end{array}\right| & x_1-x_2\\

x_3 & y_3\\

x_4 & y_4

\end{array}\right| & x_3-x_4

\end{array}\right|

}

{

x_1-x_2 & y_1-y_2\\

x_3-x_4 & y_3-y_4

}\\

y=\frac{

\end{array}\right| & y_1-y_2\\

\end{array}\right| & y_3-y_4

\]

判據為\(\Delta=

\end{array}\right|\),有:

\[\Delta\begin{cases}

=0 & no\space intersection\\

\ne0 & intersection

\end{cases}

為減少計算,讀入直線時預存\(x_1-x_2,y_1-y_2\)和\(\left|\begin{array}{}x_1 & x_2\\y_1 & y_2\end{array}\right|\)。

LineCircleIntersect

  對于\(L\{(x_1,y_1),(x_2,y_2)\},C\{(x_c,y_c),r\}\),交點為:

Dd_y\pm sign(d_y)d_x\sqrt{r^2{d_r}^2-D^2}

}{

{d_r}^2

}+x_c\\

-Dd_x\pm\left|d_y\right|\sqrt{r^2{d_r}^2-D^2}

}+y_c

其中:

\[d_x=x_2-x_2\\

d_y=y_2-y_1\\

d_r=\sqrt{{d_x}^2+{d_y}^2}\\

D=\left|\begin{array}{}

x_1-x_c & x_2-x_c\\

y_1-y_c & y_2-y_c

\end{array}\right|\\

sign(x)=\begin{cases}

-1 & x<0\\

1 & otherwise

判據為\(\Delta=r^2{d_r}^2-D^2\),有:

<0 & no\space intersection\\

=0 & tangent\\

>0 & intersection

為減少計算,讀入圓時預存\(r^2\)。

CircleCircleIntersect

  對于\(C_1\{(x_1,y_1),r_1\},C_2\{(x_2,y_2),r_2\}\),交點為:

\[x=x_0\pm h(y_2-y_1)/d_c\\

y=y_0\mp h(x_2-x_1)/d_c

\[h=\sqrt{r_1^2-a^2}\\

d_c=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\\

a=({r_1}^2 - {r_2}^2+{d_c}^2)/d_c\\

x_0=x_1+a(x_2-x_1)/d_c\\

y_0=y_1+a(y_2-y_1)/d_c

判據為\(\Delta={r_1}^2-a^2\),有:

參考資料:

https://mathworld.wolfram.com/Line-LineIntersection.html

https://mathworld.wolfram.com/Circle-LineIntersection.html

http://paulbourke.net/geometry/circlesphere/

支援新增幾何對象

  對射線和線段的支援,在不改變求交點算法的前提下實作。在求出交點後由

Line

對象使用成員函數

online

根據

m_type

判斷交點是否在該線上,決定是否将其加入交點集

m_allIntersections

中。其中:

\[(x,y)\in Segment\{(x_1,y_1),(x_2,y_2)\}\space iff\space min(x_1,x_2)\le x\le max(x_1,x_2)\and min(y_1,y_2)\le y\le max(y_1,y_2)\\(x,y)\in Ray\{(x1_1,y_1),(x_2,y_2)\}\space iff\space (x_2-x_1,y_2-y_1)·(x-x_1,y-y_2)\ge0

為節省計算,在構造線段時預存橫縱坐标的最值。

  特别地,需要額外考慮非直線的線型對象之間的相交。當其判據\(\Delta\)為零時(向量平行)仍可能相交。若資料合法,則隻可能相交于端點處,是以予以特判。

計算子產品的封裝

  将計算子產品封裝為動态連接配接庫後,對外接口有三。一為

readFile

函數,用于從檔案中輸入幾何對象;二為

addGeometryObject

函數,用于通過字元串輸入單個幾何對象;三為

getResult

函數,用于觸發交點的計算并傳回一個由包含所有幾何對象描述串的清單和由所有交點構成的清單所組成的元組,形如

pair<vector<string>, vector<Point>>

5. UML圖

UML圖是用來表示類之間設計關系的圖表, 主要有以下五種關系

  • 依賴關系
  • 關聯關系
  • 聚合關系
  • 組合關系
  • 繼承關系

用UML圖表示計算子產品的關系如下圖所示

2020軟體工程-結對項目作業

一共用三個類來存資料, Line, Circle, Node(點), Intersect裡面聚合了這三種資料, 進行一系列運算

6. 子產品性能改進

記錄在改進計算子產品性能上所花費的時間,描述你改進的思路,并展示一張性能分析圖(由VS 2015/2017的性能分析工具自動生成),并展示你程式中消耗最大的函數。(3')

交點存儲資料結構的選擇

  通過随機生成的資料進行性能測試,占用CPU時間最多的子函數往往是與交點存儲所用資料結構相關的函數,如選用

unordered_set

時哈希值的計算,選用

set

時元素之間的比較等。是以交點存儲資料結構的選擇對性能的提升十分關鍵,備選方案及可能的問題如下:

  • unordered_set

    :需要考慮哈希桶的數量(根據幾何對象數目确定)和哈希函數的設計。
  • set

    :雖然紅黑樹的更新較哈希表慢,但不存在rehash的問題。
  • vector

    +

    sort

    unique

    :重複的交點較多時會超過記憶體限制,需要随時檢測去重。

是以最終的問題在于在給定的資料條件下哪種資料結構更優。經過比較,采用了

unordered_set

,并在

Intersect

類初始化時使用

reserve

函數預設哈希桶數量為\(5000000\)以避免rehash開銷。在随機生成的\(2000\)個幾何對象的測試中(交點數在1M以上)性能分析圖如下:

2020軟體工程-結對項目作業

可見,CPU占用率最高的函數為存放交點的

unordered_set

調用的

equal_to

函數。這說明哈希值的碰撞較多,可以考慮優化哈希值的計算。查閱資料後選取了較為合适的:

\[hash(Node)=hash(Node.x)\hat{}hash(Node.y)<<1

需要注意的是,浮點數的哈希會導緻在精度範圍内相等的數被哈希至不同哈希桶,導緻重複計算。針對這個問題将哈希函數修改為:

\[hash(Node)=hash(round(Node.x * 1.0 / EPS))\hat{}hash(round(Node.y * 1.0 / EPS))

https://stackoverflow.com/questions/16792751/hashmap-for-2d3d-coordinates-i-e-vector-of-doubles

https://www.zhihu.com/question/52368555

針對C++程式的優化

  通過查閱以下材料,修改了原始程式中可能導緻性能下降的代碼,改進包含:

  • 直接執行個體化對象而非使用

    new

    來給指針配置設定堆空間,減小記憶體管理開銷。
  • 使用

    emplace

    系列函數向容器中插入元素,避免

    push

    的拷貝開銷。
  • 提前取出重複使用的成員變量避免多次訪存。
  • 取消繼承,删除指針的強制轉換,存儲時确定幾何對象類型。
  • 循環控制使用++i;

https://www.cnblogs.com/Leo_wl/p/5724620.html

https://blog.csdn.net/nodeman/article/details/80771789

https://blog.csdn.net/p942005405/article/details/84764104

7. Design By Contract

Design By Contract: It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants.

在上學期的OO中, 我了解到和約設計需要定義以下的條件

  • 前置條件 函數的輸入應該滿足什麼條件
  • 不變式 在函數計算過程中 應該滿足哪些恒定的條件
  • 後置條件 函數的結束時應滿足什麼條件

由于對于每個函數用和約設計過于複雜, 并不實用, 是以我們對于子產品進行和約設計, 各自保證計算子產品和UI子產品的正确性, 獨立測試。

8. 計算子產品部分單元測試展示

展示出項目部分單元測試代碼,并說明測試的函數,構造測試資料的思路。并将單元測試得到的測試覆寫率截圖,發表在部落格中。要求總體覆寫率到 90% 以上,否則單元測試部分視作無效。(6')

  本次單元測試繼承了上次的測試代碼,放入

OldTest

類中。其包含三個

TEST_METHOD

,分别在僅有直線、僅有圓以及線圓混合的情況下針對幾何對象之間的相離、相切、相交、共交點等情況各測試\(5\)個樣例。其中測試直線的部分如下:

2020軟體工程-結對項目作業

  新增功能的測試則包含在

NewTest

類中,其下轄三個

TEST_METHOD

針對射線和線段之間的位置關系及特殊情況(平行交于端點)共測試了\(14\)個樣例。其中測試線段的部分如下:

2020軟體工程-結對項目作業

  此外還設定了精度和溢出測試。精度損失來源于

sqrt

操作;溢出是當坐标接近\(10^5\)時,\(\Delta=r^2{d_r}^2-D^2\)等判據可能超過

long long

的表示範圍,是以圓相關一切計算改用

double

進行。

  測試樣例完全覆寫了核心計算代碼:

2020軟體工程-結對項目作業

9. 計算子產品部分異常處理說明

在部落格中詳細介紹每種異常的設計目标。每種異常都要選擇一個單元測試樣例釋出在部落格中,并指明錯誤對應的場景。(5')
異常類型 設計目标 樣例
CMDFormatException 指令行輸入錯誤 ./intersect.exe -p xx
FileNotFoundException 輸入檔案不存在 ./intersect.exe -i nowhere -o out.txt
FileFromatException 檔案内容格式錯誤

C

L 1 1 -1

CoordinateLimitExceedException 坐标超限

1

C 999999 0 1

LineDefinitionException 兩個參數點重合 L 1 1 1 1
CircleDefinitionException 圓的半徑小于等于零 C 0 0 -1
InfinateIntersectionException 幾何對象間存在重疊

2

R 0 1 0 2

S 0 3 0 4

10. 界面子產品設計

因為之前沒有GUI程式設計的經驗, 在設計界面子產品之前,我首先調查了windows下有哪些GUI開發的庫可以使用。

1.MFC

2.QT

3.WPF

4.WTL

在調查之後, 據網上評價QT比較好用, 而且是跨平台的, 但是因為安裝檔案過大, 電腦給windows的分區不足, 最終選擇了VS自帶的MFC。

MFC有兩種設計方式, 一個是dialog app一個是document app, 我選擇的是dialog app, 開發一個視窗。

使用visual studio mfc生成dialog app後, 在資源檔案裡可以找到界面對應的檔案, 打開後如下圖, 可以添加元件

2020軟體工程-結對項目作業

我主要使用了三種元件

  • Button
  • Static text
  • Edit control

Button元件用來擷取使用者的點選操作, 每次點選會觸發一個onclick函數

void CMFCApplication1Dlg::OnBnClickedButton1()
{
CString geomLine;
edit_geo_obj.GetWindowTextW(geomLine);
CT2CA sa(geomLine);
std::string s(sa);
addGeometryObject(s);
}
           

函數内可以添加各種操作, 比如在我的界面中, 有以下幾個按鈕

  • 輸入檔案
  • 增加幾何對象
  • 删除幾何對象
  • 計算交點(并且繪制)
  • 關閉

static text元件用來展示文字

Edit control是一個文本框, 可以用來輸入文字, 每個文本框會有一個變量, 用來擷取文字。

比如

file.GetWindowTextW(geomline)

就是把檔案輸入框中的文字讀入到geomLine這個Ctring中

最後, 點選計算交點後, 會把從計算子產品得到的交點繪制出來, 調用draw函數畫在GUI的中央, 如下圖所歐式

2020軟體工程-結對項目作業

11. 界面子產品與計算子產品的對接

由于我們的接口比較少, 在界面子產品對接時很流暢。

得到intersectProjectDll.lib和Interface.h後, 我看到有三個接口

__declspec(dllexport) void readFile(string);
 
__declspec(dllexport) void addGeometryObject(string);

__declspec(dllexport) void removeGeometryObject(string);

__declspec(dllexport) pair<vector<string>, vector<Point>> getResult();
           

這三個接口是我們之前商定好的接口, 盡量隐藏内部實作細節, 隻暴露三個接口

  • readFile從檔案中讀取集合對象
  • addGeometryObject添加幾何對象
  • getResult用來擷取目前的所有交點和幾何對象, 用于畫圖

功能如下圖

2020軟體工程-結對項目作業

圖中有三個按鈕, 輸入檔案調用

readFile

, 添加幾何對象調用

addGeometryObject

, 計算交點調用

getResult

并且繪制

實作以上功能後, 對接工作完成。

12. 描述結對的過程

使用工具: 騰訊會議

具體過程:

  • 在結對程式設計過程中, 我們首先沒有區分領航員和駕駛員的角色, 而是整體對于項目進行讨論, 比如用什麼樣的架構, UI的架構等。
  • 在明确了分工之後, 我們進入了探索期, 查閱文檔, 了解技術細節, 并且寫設計文檔
  • 完成了需求分析和設計後, 我們開始了幾輪的編碼。 一個人程式設計, 另外一個人負責測試和提修改意見, 經過幾輪疊代後, 我們有了基本可用的版本。
  • 在複審, 測試階段, 我們輪流提供測試樣例, 進行壓力測試
  • 最後一個階段是代碼優化, 我們采取的模式是一個人負責找優化方法, 一個人負責實作, 及時保持溝通, 得到性能提升的回報。

13. 結對程式設計優缺點

結對程式設計的優點:

  • 兩個人可以互相學習, 完善對c++文法, 标準庫使用等知識
  • 結對工作可以提升代碼的品質
  • 縮短了複審周期, 在公司的code review階段, 有時因為tech lead事情繁忙, 無法給出fd, 一段code review會經曆1-2天才能進入下階段的持續開發測試, 結對程式設計可以加速這一流程
  • 有同伴一起程式設計可以讓人更專注

結對程式設計的缺點:

  • 兩個人同時做一件事, 有些浪費時間, 不利于團隊的沖刺
  • 往往領航員數量少, 駕駛員多, 成本高

在結對程式設計的時候, 每一個人的優缺點如下:

對駕駛員的優點

  • 代碼可以及時得到回報, 架構問題可以及時調整
  • 可以找人讨論, 提升工作專注度
  • 快速學習, 能夠迅速提升軟體品質

對駕駛員的缺點

  • 有時候旁邊有人會緊張, 影響工作狀态

對領航員的優點

  • 可以了解junior一些的SDE的開發曆程
  • 統一項目代碼風格
  • 鞏固自己代碼架構, 算法知識

對領航員的缺點

  • 浪費了太多時間

14.子產品互換

我們互換的組是 https://www.cnblogs.com/starmiku/p/12560565.html

我們選擇的組和我們有相似的接口, 在替換核心子產品的時候經曆了一下幾個步驟:

1.修改dll, lib路徑, 成功引入頭檔案, 能夠連結到庫

2.修改輸入檔案, 添加幾何對象, 計算交點的接口名稱

3.運作

運作結果如下:

2020軟體工程-結對項目作業