要求:用 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
}
二. 数据类型设计
type Point struct {
x float64
y float64
}
type LinearObject struct {
//默认a点的x值小于b点的x值
a Point
b Point
}
type LineSegmentObject struct {
//持有LinearObject对象
ls_lo LinearObject
}
type Ray struct {
r_lo LinearObject
}
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
}
//定义两点
pointA := Point{x:200.0,y:601.0}
pointB := Point{x:300.0,y:901.0}
//定义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 中,方法放在接口中,并且一个类型可以实现多个接口,这是一种组合,组合比继承更灵活;同时多个类型可以实现同一个接口,实现了多态。能做到多态,就进一步确定是面向对象的语言。
要学习的东西还很多,慢慢来,比较快。