天天看點

軟工結對項目作業

軟工結對項目作業

個人項目作業——交點檢測

項目 内容
這個作業屬于哪個班級 2020春季計算機學院軟體工程(羅傑 任健)
這個作業的要求在哪裡 結對項目作業
教學班級 005
GitHub項目位址 https://github.com/SpookyDreamer/Intersect2

PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 10 20
· Estimate · 估計這個任務需要多少時間
Development 開發 990 1320
· Analysis · 需求分析 (包括學習新技術) 180 300
· Design Spec · 生成設計文檔 90 60
· Design Review · 設計複審 (和同僚稽核設計文檔) 30
· Coding Standard · 代碼規範 (為目前的開發制定合适的規範)
· Design · 具體設計 120
· Coding · 具體編碼 480
· Code Review · 代碼複審
· Test · 測試(自我測試,修改代碼,送出修改)
Reporting 報告 100
· Test Report · 測試報告
· Size Measurement · 計算工作量
· Postmortem & Process Improvement Plan · 事後總結, 并提出過程改進計劃
合計 1100 1440

看教科書和其它資料中關于 Information Hiding,Interface Design,Loose Coupling 的章節,說明你們在結對程式設計中是如何利用這些方法對接口進行設計的。

  • Information Hiding,是指在設計和确定子產品時,使得一個子產品内包含的特定資訊(過程或資料),對于不需要這些資訊的其他子產品來說,是不可通路的。在設計的過程中,我們使每一個類的屬性都是private的,外部不能直接通路,需要通過get函數來擷取屬性的值。
  • Interface Design,應遵循以下原則:單一職責原則、裡氏替換原則、依賴倒置原則、接口隔離原則、迪米特法則、開閉原則等。我們在設計的過程中,對每一個類的函數和需要的屬性,對外部開放了接口來使用。
  • Loose Coupling,通常是基于消息的系統,此時用戶端和遠端服務并不知道對方是如何實作的。用戶端和服務之間的通訊由消息的架構支配。隻要消息符合協商的架構,則用戶端或服務的實作就可以根據需要進行更改,而不必擔心會破壞對方。

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

與上一次的項目相比,本次項目在

Point

類裡并沒有什麼變化。在

Line

類裡,我們對

Line

類對象新增了兩個

Point

類的屬性和一個

char

類型的屬性,

Point

類的屬性用來存儲輸入的兩個點,

char

類的屬性用來存儲直線的類型。

與此同時,我們增加了幾個方法:

  • isOnline

    函數,用于判斷一個點是否在直線上。
  • getType

    函數,用于擷取type屬性。
  • getPoint1

    getPoint2

    函數,用于分别擷取

    point1

    point2

  • equals

    函數,用于判斷兩條直線是否相同。
  • inOneLine

    函數,用于判斷兩條直線是否共線。
class Line
{
protected:
	char type;
	double A = 0;
	double B = 0;
	double C = 0;
	Point* point1;
	Point* point2;

public:
	Line(Point* point1, Point* point2);

	virtual bool isParallel(Line* line);

	virtual Point* intersect(Line* line);

	virtual bool isOnline(Point* point);

	virtual bool equals(Line* line);

	virtual char getType();

	virtual bool inOneLine(Line* l);

	virtual Point* getPoint1();

	virtual Point* getPoint2();
};
           

對于射線

Radial

類,我們讓它繼承于

Line

類。重寫了Line類的

isOnline

inOneLine

equals

函數。同時新增了一個方法。

  • countIntersectinOneLine

    函數,用于判斷兩條射線是有唯一交點或是沒有交點或是有無數交點。
class Radial :public Line
{
public:
	Radial(Point* point1, Point* point2) :Line(point1, point2) {
		type = 'R';
	}

	virtual bool isOnline(Point* point);

	virtual bool equals(Line* line);

	virtual bool inOneLine(Radial* r);

	virtual int countIntersectinOneLine(Radial* r);
};
           

線段Segment類則繼承于Radial類。重寫了Radial類中的

isOnline

equals

countIntersectinOneLine

三個函數。

class Segment :public Radial
{
public:
	Segment(Point* point1, Point* point2) :Radial(point1, point2){
		type = 'S';
	}

	virtual bool isOnline(Point* point);

	virtual bool equals(Line* line);

	virtual int countIntersectinOneLine(Radial* r);

};
           

UML類圖

由于類的數量比較多,在這裡對其中的方法進行了折疊。

軟工結對項目作業

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

構造單元測試時基本思路是對每一個類中的方法進行測試,注重代碼的細節邏輯,争取覆寫全面。

部分單元測試的代碼:

  • 測試同一直線上兩線交點個數
軟工結對項目作業
  • 測試容器類的:
軟工結對項目作業

單元測試通過截圖:

軟工結對項目作業

單元測試覆寫率截圖:沒有找到合适的單元測試覆寫率的檢查工具。

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

在異常處理部分,通過自定義4種異常類,我們完成了4種異常的處理

WrongFormatException //格式錯誤:檔案中的幾何對象描述不正确,不能被正确的解析。
UnableToConstructException //給出的幾何對象上兩點重複,無法構造直線/線段/射線。
CoordinateOutOfRangeException //坐标超過了題目要求的範圍。
InfiniteIntersectionPointsException //當插入一個新的幾何圖形時,會導緻無窮個交點的出現。 
           

異常的應用場景以及單元測試。

  • WrongFormatException

    這個異常出現在從檔案中導入集合對象的描述時,描述的格式與題目要求不符。
    char type;
    double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    		try {
    			if (getline(input, strLine)) {
    				//split line
    				vector<string> res;
    				stringstream aline(strLine);
    				string result;
    				while (aline >> result)
    					res.push_back(result);
    				//exception
    				if (res.size() != 5) {
    					throw WrongFormatException();
    				}
    				stringstream countstr(res[0]);
    				if (!(countstr >> type)) {
    					throw WrongFormatException();
    				}
    				if ((type!='L') && (type != 'R') && (type != 'S')) {
    					throw WrongFormatException();
    				}
    				stringstream x1str(res[1]), y1str(res[2]), x2str(res[3]), y2str(res[4]);
    				if ((!(x1str >> x1)) || (!(y1str >> y1)) || (!(x2str >> x2)) || (!(y2str >> y2))) {
    					throw WrongFormatException();
    				}
    			}
    			else {
    				throw WrongFormatException();
    			}
               
    這類異常沒有進行單元測試。進行的測試主要是改變input.txt中的内容,通過指令行的運作,觀察輸出的錯誤資訊是否一緻。
  • UnableToConstructException

    這個異常出現在構造幾何對象(直線/線段/射線)時。當出現這種錯誤時,無法構造直線/線段/射線。
    if (point1->equals(point2)) {
    		throw UnableToConstructException();
    	}
               
    單元測試:
    Point* pe4 = new Point(0, 0);
    			try {
    				Line* l3 = new Line(pe4, pe4);
    				Assert::IsFalse(true);
    			}
    			catch (UnableToConstructException& e) {
    				Assert::AreEqual(e.what(), "ERROR! Since the two points given coincide, we cannot construct a line.\n");
    			}
               
  • CoordinateOutOfRangeException

    這個異常出現在構造幾何對象(直線/線段/射線)時。這種情況不符合題目要求
    if (x1 >= 100000 || x1 <= -100000 || y1 >= 100000 || y1 <= -100000 || x2 >= 100000 || x2 <= -100000 || y2 >= 100000 || y2 <= -100000) {
    		throw CoordinateOutOfRangeException();
    	}
               
    Point* pe1 = new Point(0, 0);
    Point* pe2 = new Point(-100000, 0);			
    	try {
            Line* l1 = new Line(pe1, pe2);
    		Assert::IsFalse(true);
    	}
    	catch (CoordinateOutOfRangeException& e) {
    				Assert::AreEqual(e.what(), "ERROR! Both horizontal and vertical values should be within  (-100000, 100000).\n");
    			}
               
  • InfiniteIntersectionPointsException

    這個異常出現插入新的幾何對象時。當新插入的這個圖形會導緻目前的幾何對象集會出現無窮多個交點的時候,則不應該将其插入集合中。
    char type1 = l->getType();
    if (!line->inOneLine(l)) {
    	continue;
    }
    if (line->getType() == 'L' || l->getType() == 'L') {
    	throw InfiniteIntersectionPointsException();
    }
    Radial* r0 = (Radial*)line;
    Radial* r1 = (Radial*)l;
    int tmp = r0->countIntersectinOneLine(r1);
    if (tmp < 0) {
    	throw InfiniteIntersectionPointsException();
    }
               
    LineSeries* ls0 = new LineSeries();
    Point* pe5 = new Point(0, 0);
    Point* pe6 = new Point(1, 1);
    Line* le1 = new Line(pe5, pe6);
    Segment* se1 = new Segment(pe5, pe6);
    ls0->add_line(se1);
    try {
    	ls0->add_line(le1);
    	Assert::IsFalse(true);
    }
    catch (InfiniteIntersectionPointsException& e) {
    	Assert::AreEqual(e.what(), "ERROR!There are infinite points of intersection between geometric objects .\n");
    }
               

Design by Contract,Code Contract

契約式設計或者Design by Contract (DbC)是一種設計計算機軟體的方法。這種方法要求軟體設計者為軟體元件定義正式的,精确的并且可驗證的接口,這樣,為傳統的抽象資料類型又增加了先驗條件、後驗條件和不變式。這種方法的名字裡用到的“契約”或者說“契約”是一種比喻,因為它和商業契約的情況有點類似。

  • 優點
    • 獲得更優秀的設計
    • 提高可靠性
    • 更出色的文檔
    • 簡化調試
    • 支援複用
  • 開銷和限制
    • 開發這需要時間學習撰寫良好契約的思想和技術
    • 需要大量的實踐

界面子產品的詳細設計過程

在UI子產品的設計上,我們使用了vs基于Qt的擴充。在圖像的繪制上面,我使用了

QCustomPlot

類中的

AbstractItem

對圖像進行直接的繪制。最後完成的UI界面如圖所示:

軟工結對項目作業

UI界面一共有6個按鈕,具體功能分别為:

  • Init

    :清空容器;
  • File

    :讀取檔案,并在下方輸出讀取檔案的内容;
  • Add

    :添加下方所描述的直線;
  • Delete

    :删除下方所描述的直線;
  • Calculate

    :計算交點數量并在下方輸出;
  • Paint

    :畫出目前容器中所有直線的圖像。

所對應的槽函數如下所示:

private slots:

	void init();

	void addLine();

	void deleteLine();

	void calculate();

	void readFile();

	void paintLine();
           

下面我将以

addLine()

為例,來具體地讨論槽函數的實作:

void QtGuiApplication1::addLine()
{
	QString str = ui.text->toPlainText();//讀取textedit中的内容
	QStringList list = str.split(" ");//用空格将讀取到的字元串分割

	double x1 = list.at(1).toDouble();
	double y1 = list.at(2).toDouble();
	double x2 = list.at(3).toDouble();
	double y2 = list.at(4).toDouble();
	Point* point1 = new Point(x1, y1);
	Point* point2 = new Point(x2, y2);//建立Point類對象
	
	//分類讨論直線類型,建立相應的對象,并将其加入到容器中
	if (QString::compare(list.at(0), "L") == 0) {
		Line* line = new Line(point1, point2);
		ls->add_line(line);
	}
	else if (QString::compare(list.at(0), "S") == 0) {
		Line* line = new Segment(point1, point2);
		ls->add_line(line);
	}
	else if (QString::compare(list.at(0), "R") == 0) {
		Line* line = new Radial(point1, point2);
		ls->add_line(line);
	}
	//更新calculate和paint的結果
	calculate();
	paintLine();
}
           

首先讀取

Qtextedit

工具裡的字元串并将其分割,儲存在

list

中。然後根據得到的輸入的類型,分類讨論所輸入的直線屬于哪一類,并調用

core

子產品的

add_list

函數,将其加入到容器中。最後更新

calculate

paint

的結果。

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

在計算子產品中,我們設計了一個容器類

LineSeries

并在其中設計了與UI子產品的接口。計算子產品的接口如下所示:

LineSeries();

virtual void add_line(Line* line);

virtual void delete_line(Line* line);

virtual void clear();

virtual int cal_intersects();

virtual std::unordered_set<Point*, Hash_point, Equal_point> getIntersects();

virtual std::vector<Line*> getLines();
           
  • LineSeries()

    為容器類,負責存儲直線和交點以及進行下面的處理;
  • add_line(Line* line)

    ,添加直線;
  • delete_line(Line* line)

    ,删除直線;
  • clear()

    ,清空容器;
  • cal_intersects()

    ,計算交點并傳回交點個數;
  • getIntersects()

    ,擷取交點集合;
  • getLines()

    ,擷取直線集合;

在UI子產品的設計中,我們調用了計算子產品的接口。最終實作了要求的功能:

  1. 支援從檔案導入幾何對象的描述。
  2. 支援幾何對象的添加、删除。
  3. 支援繪制現有幾何對象。
  4. 支援求解現有幾何對象交點。

結對過程的描述

在進行結對項目的過程中,我們使用了VS2019自帶的

liveshare

功能:

軟工結對項目作業

并且通過微信進行了語音和文字兩種方式的交流:

軟工結對項目作業

結對程式設計的優缺點

  • 結對程式設計
      • 在開發層次,結對程式設計能提供更好的設計品質和代碼品質,兩人合作能有更強的解決問題的能力。
      • 對開發人員自身來說,結對工作能帶來更多的信心,高品質的産出能帶來更高的滿足感。
      • 在心理上, 當有另一個人在你身邊和你緊密配合, 做同樣一件事情的時候, 你不好意思開小差, 也不好意思糊弄。
      • 在企業管理層次上,結對能更有效地交流,互相學習和傳遞經驗,能更好地處理人員流動。因為一個人的知識已經被其他人共享。
    • 缺點
      • 兩個人的學習習慣和程式設計習慣不同,需要磨合。
      • 容易産生“過讨論”的狀況,花費過多的時間在讨論上面而沒有切實地進行工作。
      • 學習能力較強,對于新鮮事物的接受能力比較好。
      • 程式設計習慣比較好。
      • 執行力強。
      • c++基礎比較差。
      • 遇到困難容易氣餒,耐心不足。
  • 結對夥伴
      • c++基礎較好。
      • 思考較為缜密,在測試方面比較細心。
      • 比較有耐心。
      • 交流不夠主動。