要求:用 Go 語言實現直線,射線,線段的實例程序,並滿足 SRP,OCP 設計原則。
一. 接口設計
-
Oner 接口,也是該程序最核心的接口
type Oner interface { isOn(p Point) bool }
-
Pointer 接口,實現該接口可以返回點的 x 和 y 的值
type Pointer interface { //獲取點的x座標 getX() float64 //獲取點的y座標 getY() float64 }
-
Linear 接口,計算斜率,直線截距,返回兩點對象
type Linear interface { //計算直線斜率 getSlope () float64 //計算直線截距 getIntercept () float64 //獲取點A getA () Point //獲取點B getB () Point }
-
LineSegment 接口,獲取線段長度,判斷給定點是否在線段上
type LineSegment interface { //同樣需要實現Oner接口,判斷是否在線段上 Oner //獲取線段長度 getLength() float64 }
二. 數據類型設計
- Point 類型,x 和 y 屬性分別代表 x 軸和 y 軸的值,需要實現 Pointer 接口
type Point struct { x float64 y float64 }
- LinearObject 類型,a 和 b 爲給定的兩點,數據類型爲 Point,需要實現 Linear 接口
type LinearObject struct { //默認a點的x值小於b點的x值 a Point b Point }
- LineSegmentObject 類型,需要實現 LineSegment 接口
type LineSegmentObject struct { //持有LinearObject對象 ls_lo LinearObject }
- Ray 類型,需要實現 Oner 接口
type Ray struct { r_lo LinearObject }
- Line 類型,需要實現 Oner 接口
type Line struct { l_lo LinearObject }
三. 接口實現,實現接口中所有方法即實現了該接口
-
Ponit 類型實現 Pointer 接口
func (p Point) getX() float64 { return p.x } func (p Point) getY() float64 { return p.y }
-
LinearObject 類型實現 Linear 接口
//計算斜率 func (lo LinearObject) getSlope() float64 { var slop float64 if lo.a.getX() == lo.b.getY() { slop = 1 } else { slop = (lo.a.getY() - lo.b.getY()) / (lo.a.getX() - lo.b.getX()) } return slop } //計算直線截距 func (lo LinearObject) getIntercept() float64 { var intercept float64 if lo.getSlope() == 1 { intercept = 0 } else { intercept = lo.a.getY() - lo.getSlope() * lo.a.getX() } return intercept } //獲取點A func (lo LinearObject) getA() Point { return lo.a } //獲取點B func (lo LinearObject) getB() Point { return lo.b }
-
LineSegmentObject 類型實現 LineSegment接口和 Oner 接口
//是否在線段上 func (lso LineSegmentObject) isOn(o Point) bool { var isInSegment bool = false //首先判斷該點是否在LinearObject對象中兩點構成的矩形中 minX := math.Min(lso.ls_lo.a.x,lso.ls_lo.b.x) maxX := math.Max(lso.ls_lo.a.x,lso.ls_lo.b.x) minY := math.Min(lso.ls_lo.a.y,lso.ls_lo.b.y) maxY := math.Max(lso.ls_lo.a.y,lso.ls_lo.b.y) //該點在兩點構成的矩形中 if (o.x >= minX && o.x <= maxX) && (o.y >= minY && o.y <= maxY) { //判斷斜率是否相等 if (o.getY() - lso.ls_lo.getA().getY()) / (o.getX() - lso.ls_lo.getA().getX()) == lso.ls_lo.getSlope() { isInSegment = true } } return isInSegment } //獲取線段長度 func (lso LineSegmentObject) getLength() float64 { var sum float64 = 0.0 sum = sum + math.Pow((lso.ls_lo.getA().getX() - lso.ls_lo.getB().getX()),2) sum = sum + math.Pow((lso.ls_lo.getA().getY() - lso.ls_lo.getB().getY()),2) sum = math.Sqrt(sum) return sum }
-
Ray 類型實現 Oner 接口
//判斷點是否在射線上 func (r Ray) isOn(o Point) bool { //由於不清楚射線的方向,暫時將x較小的點作爲端點 //斜率不小於0 if r.r_lo.getSlope() >= 0 { //排除非法區間 if o.getX() < r.r_lo.a.getX() || o.getY() < r.r_lo.a.getY() { return false } } else { //排除非法區間 if o.getX() < r.r_lo.a.getX() || o.getY() > r.r_lo.a.getY() { return false } } //區間合法,判斷斜率是否相等 if (o.getY() - r.r_lo.getA().getY()) / (o.getX() - r.r_lo.getA().getX()) == r.r_lo.getSlope() { return true } else { return false } }
-
Line 類型實現Oner接口
//判斷點是否在直線上 func (l Line) isOn(o Point) bool { if (o.getY() - l.l_lo.getA().getY()) / (o.getX() - l.l_lo.getA().getX()) == l.l_lo.getSlope() { return true } else { return false } }
四. 測試部分
測試以直線 y = 3 * x + 1 作爲參考,其中 x 取值採用隨機函數生成 [0,500) 之間的整數並轉換爲 float64 類型。y 的取值包括隨機產生值 y1 和上面直線的計算值 y2 兩種方式,最後僅有一個 y 值,在 y1 和 y2 中隨機挑選。在判斷點是否在射線上時,有如下約定:取 x 較小值的點作爲端點。測試樣例一共 100 個。
- 定義數組存放測試點,隨機產生測試點
//定義數組,大小100,存放隨機數,用於測試 var pointArr [100] Point //產生隨機數,設置種子(當前系統時間) rand.Seed(time.Now().UnixNano()) for i := 0; i < 100 ; i++ { //範圍在0到500 x1 := float64(rand.Intn(500)) y1 := 3 * x1 + 1 y2 := float64(rand.Intn(500)) //在y1和y2中隨機選取一個(y1在直線上,y2不一定) s := rand.Intn(2) var p Point if s == 0 { p = Point{x:x1,y:y1} } else { p = Point{x:x1,y:y2} } pointArr [i] = p }
- 定義點 A 和 點 B,確定直線
//定義兩點 pointA := Point{x:200.0,y:601.0} pointB := Point{x:300.0,y:901.0}
- 定義 LinearObject,線段對象,射線對象,直線對象
//定義LinearObject,由兩點組成 linearObject := LinearObject{a:pointA,b:pointB} //定義線段對象 linearSegmentObject := LineSegmentObject{ls_lo:linearObject} //定義射線對象 ray := Ray{r_lo:linearObject} //定義直線對象 line := Line{l_lo:linearObject}
- 測試代碼
//測試所有點,輸出測試結果 for i, p := range pointArr { var isOnLine Oner isOnLine = line fmt.Print("點C(",pointArr[i].x) fmt.Print(",",pointArr[i].y) fmt.Println(")是否在點A和點B構成的直線上:",isOnLine.isOn(p)) //射線和直線接口對象 var isOnRay Oner isOnRay = ray fmt.Print("點C(",pointArr[i].x) fmt.Print(",",pointArr[i].y) fmt.Println(")是否在點A和點B構成的射線上:",isOnRay.isOn(p)) //線段接口對象 var lineSegment LineSegment lineSegment = linearSegmentObject fmt.Print("點C(",pointArr[i].x) fmt.Print(",",pointArr[i].y) fmt.Println(")是否在點A和點B構成的線段上:",lineSegment.isOn(p)) fmt.Println() }
五. 部分測試結果截圖
六. 總結
說說對 Go 語言使用的體會,花了一個下午的時間學習了一下 Go 語言,上手還是很輕鬆的,和 C 語言很相似,因爲沒有類這個概念,一度讓我覺得他是一門面向過程的語言,直到我看到了接口這部分內容,才確定 Go 語言是一門面向對象的語言,但作爲面嚮對象語言他卻沒有繼承這種概念,在 Java,Dart 等語言中,如果你想實現某個接口會用到 implements 關鍵字,而這在 Go 中是不存在的,Go 選擇了隱式的方式實現接口,即當你把一個接口中的所有方法都實了,你就已經實現了該接口。因爲沒有類,所以應該也不能有對象,取而代之的是 struct,這是一種類型。在 Java 中,一個類中有屬性和方法,而在 Go 中將屬性和方法分離,屬性放在 struct 中,方法放在接口中,並且一個類型可以實現多個接口,這是一種組合,組合比繼承更靈活;同時多個類型可以實現同一個接口,實現了多態。能做到多態,就進一步確定是面向對象的語言。
要學習的東西還很多,慢慢來,比較快。