用 Go 語言實現直線,射線,線段的實例程序,並滿足 SRP,OCP 設計原則。

要求:用 Go 語言實現直線,射線,線段的實例程序,並滿足 SRP,OCP 設計原則。

一. 接口設計

  1. Oner 接口,也是該程序最核心的接口

    type Oner interface {
    	isOn(p Point) bool
    }
    
  2. Pointer 接口,實現該接口可以返回點的 x 和 y 的值

    type Pointer interface {
    	//獲取點的x座標
    	getX() float64
    	//獲取點的y座標
    	getY() float64
    }
    
  3. Linear 接口,計算斜率,直線截距,返回兩點對象

    type Linear interface {
    	//計算直線斜率
    	getSlope () float64
    	//計算直線截距
    	getIntercept () float64
    	//獲取點A
    	getA () Point
    	//獲取點B
    	getB () Point
    }
    
  4. LineSegment 接口,獲取線段長度,判斷給定點是否在線段上

    type LineSegment interface {
    	//同樣需要實現Oner接口,判斷是否在線段上
    	Oner
    	//獲取線段長度
    	getLength() float64
    }
    

二. 數據類型設計

  1. Point 類型,x 和 y 屬性分別代表 x 軸和 y 軸的值,需要實現 Pointer 接口
    type Point struct {
    	x float64
    	y float64
    }
    
  2. LinearObject 類型,a 和 b 爲給定的兩點,數據類型爲 Point,需要實現 Linear 接口
    type LinearObject struct {
    	//默認a點的x值小於b點的x值
    	a Point
    	b Point
    }
    
  3. LineSegmentObject 類型,需要實現 LineSegment 接口
    type LineSegmentObject struct {
    	//持有LinearObject對象
    	ls_lo LinearObject
    }
    
  4. Ray 類型,需要實現 Oner 接口
    type Ray struct {
    	r_lo LinearObject
    }
    
  5. Line 類型,需要實現 Oner 接口
    type Line struct {
    	l_lo LinearObject
    }
    

三. 接口實現,實現接口中所有方法即實現了該接口

  1. Ponit 類型實現 Pointer 接口

    func (p Point) getX() float64 {
    	return p.x
    }
    
    func (p Point) getY() float64 {
    	return p.y
    }
    
  2. 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
    }
    
  3. 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
    }
    
  4. 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
    	}
    }
    
  5. 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 中,方法放在接口中,並且一個類型可以實現多個接口,這是一種組合,組合比繼承更靈活;同時多個類型可以實現同一個接口,實現了多態。能做到多態,就進一步確定是面向對象的語言。

要學習的東西還很多,慢慢來,比較快。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章