2020軟件工程-結對項目做業

2020軟件工程-結對項目做業

1. 項目簡介

github連接 html

項目 內容
這個做業屬於哪一個課程 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 需求分析 0 0
Desing Spaec 生成設計文檔 100 40
Design Review 設計複審 100 10
Coding Standard 代碼規範 0 0
Design 具體設計 60 40
Coding 具體編碼 1100 180
Code Review 代碼複審 60 60
Test 測試 180 180
Reporting 報告 100 120
Test Report 測試報告 0 0
Size Measurement 計算工做量 5 0
Postmortem & Process Improvement 過後總結 60 50
- 合計 1800 1700

3. 接口設計

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

  • 單一職責原則c++

    Single Responsibility Principle: There shuld never be more than one reason for class to changegit

    單一職責原則要求一個接口, 一個功能, 若是在private 方法和public接口中, 改動過多, 那麼容易引發錯誤。 在結對編程的設計中, 計算模塊裏咱們儘可能保證一個函數只作一個事情, 好比Line Circle Point分出三個類, 每一個求交點的函數只處理一種類型等。github

  • 里氏替換原則算法

    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繼承父類的方式實現, 後來發現這種繼承影響性能, 就放棄了繼承。windows

  • 依賴倒置原則數據結構

    依賴倒轉原則是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函數獲得交點個數,或使用GetGeoObjectsGetIntersections函數輸出幾何對象的具體信息,也可經過ViewIntersections函數打印交點至標準輸出以快速調試。

  主類在實例化時生成存放幾何對象及交點的容器,並在後續過程當中不斷更新。UML圖以下:

交點計算

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

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}{} \left|\begin{array}{} x_1 & y_1\\ x_2 & y_2 \end{array}\right| & x_1-x_2\\ \left|\begin{array}{} x_3 & y_3\\ x_4 & y_4 \end{array}\right| & x_3-x_4 \end{array}\right| } { \left|\begin{array}{} x_1-x_2 & y_1-y_2\\ x_3-x_4 & y_3-y_4 \end{array}\right| }\\ y=\frac{ \left|\begin{array}{} \left|\begin{array}{} x_1 & y_1\\ x_2 & y_2 \end{array}\right| & y_1-y_2\\ \left|\begin{array}{} x_3 & y_3\\ x_4 & y_4 \end{array}\right| & y_3-y_4 \end{array}\right| } { \left|\begin{array}{} x_1-x_2 & y_1-y_2\\ x_3-x_4 & y_3-y_4 \end{array}\right| } \]

判據爲\(\Delta= \left|\begin{array}{} x_1-x_2 & y_1-y_2\\ x_3-x_4 & y_3-y_4 \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\}\),交點爲:

\[x=\frac{ Dd_y\pm sign(d_y)d_x\sqrt{r^2{d_r}^2-D^2} }{ {d_r}^2 }+x_c\\ y=\frac{ -Dd_x\pm\left|d_y\right|\sqrt{r^2{d_r}^2-D^2} }{ {d_r}^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 \end{cases} \]

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

\[\Delta\begin{cases} <0 & no\space intersection\\ =0 & tangent\\ >0 & intersection \end{cases} \]

爲減小計算,讀入圓時預存\(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\),有:

\[\Delta\begin{cases} <0 & no\space intersection\\ =0 & tangent\\ >0 & intersection \end{cases} \]

參考資料:

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圖表示計算模塊的關係以下圖所示

一共用三個類來存數據, 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以上)性能分析圖以下:

可見,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;

http://www.javashuo.com/article/p-hvmxpkiy-nn.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\)個樣例。其中測試直線的部分以下:

  新增功能的測試則包含在NewTest類中,其下轄三個TEST_METHOD針對射線和線段之間的位置關係及特殊狀況(平行交於端點)共測試了\(14\)個樣例。其中測試線段的部分以下:

  此外還設置了精度和溢出測試。精度損失來源於sqrt操做;溢出是當座標接近\(10^5\)時,\(\Delta=r^2{d_r}^2-D^2\)等判據可能超過long long的表示範圍,所以圓相關一切計算改用double進行。

  測試樣例徹底覆蓋了核心計算代碼:

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 兩個參數點重合 1
L 1 1 1 1
CircleDefinitionException 圓的半徑小於等於零 1
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後, 在資源文件裏能夠找到界面對應的文件, 打開後以下圖, 能夠添加組件

我主要使用了三種組件

  • 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的中央, 以下圖所歐式

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用來獲取當前的全部交點和幾何對象, 用於畫圖

功能以下圖

圖中有三個按鈕, 輸入文件調用readFile, 添加幾何對象調用addGeometryObject, 計算交點調用getResult 而且繪製

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

12. 描述結對的過程

使用工具: 騰訊會議

具體過程:

  • 在結對編程過程當中, 咱們首先沒有區分領航員和駕駛員的角色, 而是總體對於項目進行討論, 好比用什麼樣的架構, UI的框架等。
  • 在明確了分工以後, 咱們進入了探索期, 查閱文檔, 瞭解技術細節, 而且寫設計文檔
  • 完成了需求分析和設計後, 咱們開始了幾輪的編碼。 一我的編程, 另一我的負責測試和提修改意見, 通過幾輪迭代後, 咱們有了基本可用的版本。
  • 在複審, 測試階段, 咱們輪流提供測試樣例, 進行壓力測試
  • 最後一個階段是代碼優化, 咱們採起的模式是一我的負責找優化方法, 一我的負責實現, 及時保持溝通, 獲得性能提高的反饋。

13. 結對編程優缺點

結對編程的優勢

  • 兩我的能夠互相學習, 完善對c++語法, 標準庫使用等知識
  • 結對工做能夠提高代碼的質量
  • 縮短了複審週期, 在公司的code review階段, 有時由於tech lead事情繁忙, 沒法給出fd, 一段code review會經歷1-2天才能進入下階段的持續開發測試, 結對編程能夠加速這一流程
  • 有同伴一塊兒編程可讓人更專一

結對編程的缺點:

  • 兩我的同時作一件事, 有些浪費時間, 不利於團隊的衝刺
  • 每每領航員數量少, 駕駛員多, 成本高

在結對編程的時候, 每個人的優缺點以下:
對駕駛員的優勢

  • 代碼能夠及時獲得反饋, 架構問題能夠及時調整
  • 能夠找人討論, 提高工做專一度
  • 快速學習, 可以迅速提高軟件質量

對駕駛員的缺點

  • 有時候旁邊有人會緊張, 影響工做狀態

對領航員的優勢

  • 能夠了解junior一些的SDE的開發歷程
  • 統一項目代碼風格
  • 鞏固本身代碼架構, 算法知識

對領航員的缺點

  • 浪費了太多時間

14.模塊互換

咱們互換的組是 http://www.javashuo.com/article/p-dumtxcfu-nn.html

咱們選擇的組和咱們有類似的接口, 在替換核心模塊的時候經歷了一下幾個步驟:

1.修改dll, lib路徑, 成功引入頭文件, 可以連接到庫

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

3.運行

運行結果以下:

相關文章
相關標籤/搜索