接口设计示例 二
这个示例与前文中的设计原则里的张三李四开车非常相似。只不过这里是咱自己总结的。。。不足的地方后续慢慢再改~
在下面的例子中,我们把 main()
中的代码当作外部代码,通过调用我们暴露出来的接口(这里的接口仅仅是一个函数),来获取想要的数据。其实就等于说,除了 main() 以外,其他的代码都当作是其他 package 内的代码。然后通过 main() 调用。在网上的很多代码示例,都是这么来搞得。
只有一个 struct
现在我想要计算一个图形周长和面积。假定现在只有一个正方形,那么我定义一个正方形
// Square 正方形的属性
type Square struct {
side float32
}
并且有两个方法来计算正方形的面积与周长
// Area 正方形求面积的方法,接收了正方形的结构体并使用结构体中的边长属性来计算面积
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
// Perimeter 正方形求周长的方法
func (sq *Square) Perimeter() float32 {
return sq.side * 4
}
并且提供一个函数,供其他人调用以便获取图形的面积与周长,只需要调用 PrintResult()
函数,并传递参数即可。
// PrintResult 输出计算结果
func PrintResult(shape string, s*Square) {
fmt.Printf("%s的面积:%.2f\n", shape, s.Area())
fmt.Printf("%s的周长:%.2f\n", shape, s.Perimeter())
}
下面这是一段完整的代码,我们可以在 main()
中调用 PrintResult()
来获取正方形的周长和面积。
package main
import "fmt"
// Square 正方形的属性
type Square struct {
side float32
}
// Area 正方形求面积的方法,接收了正方形的结构体并使用结构体中的边长属性来计算面积
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
// Perimeter 正方形求周长的方法
func (sq *Square) Perimeter() float32 {
return sq.side * 4
}
// PrintResult 输出计算结果
func PrintResult(shape string, s*Square) {
fmt.Printf("%s的面积:%.2f\n", shape, s.Area())
fmt.Printf("%s的周长:%.2f\n", shape, s.Perimeter())
}
// Formula 计算不同图形的周长和面积
func main() {
square := &Square{side: 5}
PrintResult("正方形", square)
}
增加一个 struct
现在我还想计算另一个图形的面积和周长,那么我就需要增加一个结构体和对应的方法:
// Rectangle 长方形的属性
type Rectangle struct {
length float32
width float32
}
// Area 矩形求面积的方法
func (r Rectangle) Area() float32 {
return r.length * r.width
}
// Perimeter 矩形求周长的方法
func (r Rectangle) Perimeter() float32 {
return r.length*2 + r.width*2
}
并且,此时我需要修改我本身的代码,因为PrintResult()
是用来接收正方形参数的,所以我需要添加一个函数,来接收长方形的参数。
假如我现在再添加一个用于计算长方形的函数
// PrintResult2 输出计算结果
func PrintResult2(shape string, result *Rectangle) {
fmt.Printf("%s的面积:%.2f\n", shape, result.Area())
fmt.Printf("%s的周长:%.2f\n", shape, result.Perimeter())
}
下面这是一段完整的代码,我们可以在 main()
中调用 PrintResult()
和 PrintResult2()
来获取正方形与长方形的周长和面积。
package main
import "fmt"
// Square 正方形的属性
type Square struct {
side float32
}
// Rectangle 长方形的属性
type Rectangle struct {
length float32
width float32
}
// Area 正方形求面积的方法,接收了正方形的结构体并使用结构体中的边长属性来计算面积
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
// Perimeter 正方形求周长的方法
func (sq *Square) Perimeter() float32 {
return sq.side * 4
}
// Area 矩形求面积的方法
func (r Rectangle) Area() float32 {
return r.length * r.width
}
// Perimeter 矩形求周长的方法
func (r Rectangle) Perimeter() float32 {
return r.length*2 + r.width*2
}
// PrintResult 输出计算结果
func PrintResult(shape string, result *Square) {
fmt.Printf("%s的面积:%.2f\n", shape, result.Area())
fmt.Printf("%s的周长:%.2f\n", shape, result.Perimeter())
}
// PrintResult2 输出计算结果
func PrintResult2(shape string, result *Rectangle) {
fmt.Printf("%s的面积:%.2f\n", shape, result.Area())
fmt.Printf("%s的周长:%.2f\n", shape, result.Perimeter())
}
// Formula 计算不同图形的周长和面积
func main() {
square := &Square{side: 5}
rectangle := &Rectangle{length: 4, width: 3}
PrintResult("正方形", square)
PrintResult2("矩形", rectangle)
}
这个时候,我们可以看到,我们为了提供更多的功能,而修改了原始代码。如果是一个很大的项目,不同的结构体在不同的 package 内,每增加一个新的需求,我们就需要修改暴露的接口(也就是修改 PrintResult()
),而对方在使用时,也需要使用新的函数进行调用。那么有没有一种办法,可以只对外暴露一个接口,就可以随时扩展接口内的能力呢。
这个需求,就是 Go 中 Interface(接口) 可以实现的。从这段描述中也可以看到,**implements(实现了) **是一个很关键的描述,如果想要通过一个统一的接口还获取想要的结果,那么被获取者(也就是 struct) 就要实现这个接口。而想要实现,那么 struct 就必须要实现 Interface 定义的所有方法。
统一接口
在上面的代码中,各种图形都实现了两个方法(即.计算图形面积、计算图形周长),所以我们就声明一个包含这两个方法的接口
// FormulaOperator 定义了一个接口,包含两个方法
// 该接口实现了一个求面积的方法和一个求周长的方法
type FormulaOperator interface {
Area() float32
Perimeter() float32
}
然后暴露的接口改为如下代码即可
// PrintResult 输出计算结果
func PrintResult(shape string, result FormulaOperator) {
fmt.Printf("%s的面积:%f\n", shape, result.Area())
fmt.Printf("%s的周长:%f\n", shape, result.Perimeter())
}
然后在 main() 中这样调用接口:
// Formula 计算不同图形的周长和面积
func main() {
square := &Square{side: 5}
rectangle := Rectangle{length: 4, width: 3}
PrintResult("正方形", square)
PrintResult("矩形", rectangle)
}
只需要在调用一个函数,传递参数,指定要计算的图形,即可获取改图形的周长和面积了,具体代码如下:
package main
import "fmt"
// FormulaOperator 定义了一个接口,包含两个方法
// 该接口实现了一个求面积的方法和一个求周长的方法
type FormulaOperator interface {
Area() float32
Perimeter() float32
}
// 定义了两个结构体,结构体实现了接口FormulaOperator
// 名为正方形的结构体,里面有一个参数是边长。名为矩形的结构体里面有两个参数
// Square 正方形的属性
type Square struct {
side float32
}
// Rectangle 长方形的属性
type Rectangle struct {
length float32
width float32
}
// Area 正方形求面积的方法,接收了正方形的结构体并使用结构体中的边长属性来计算面积
func (sq *Square) Area() float32 {
return sq.side * sq.side
}
// Perimeter 正方形求周长的方法
func (sq *Square) Perimeter() float32 {
return sq.side * 4
}
// Area 矩形求面积的方法
func (r Rectangle) Area() float32 {
return r.length * r.width
}
// Perimeter 矩形求周长的方法
func (r Rectangle) Perimeter() float32 {
return r.length*2 + r.width*2
}
// PrintResult 输出计算结果
func PrintResult(shape string, result FormulaOperator) {
fmt.Printf("%s的面积:%f\n", shape, result.Area())
fmt.Printf("%s的周长:%f\n", shape, result.Perimeter())
}
// Formula 计算不同图形的周长和面积
func main() {
square := &Square{side: 5}
rectangle := Rectangle{length: 4, width: 3}
PrintResult("正方形", square)
PrintResult("矩形", rectangle)
}
反馈
此页是否对你有帮助?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.