Golang初级系列教程-接口
接口是方法定义和实现之间的准则。在现实生活中,我们无形中就遵循某些接口或者是我们希望他人遵循的。当我们进入银行存钱时,我们希望银行能够帮我们安全的管理钱财并且提供额外的收益;但是我们并不关心银行如何去运作。这种隐性契约就是我们和银行之间的交互接口。
在编程语言中,接口都遵循同一种模式。下面我们会使用一种不同于 C++
、Java
、C#
等等面向对象语言的接口模式来列举几个 Go
语言的例子。Go
作为一种面向过程的语言,它的接口设计也非常好,并且能够产出很多优秀的设计。
在例子中,我们会求取各种图形,例如长方形、正常性、圆形等等的面积。我们把 Shage
作为一种接口,定义求取面积的方法,实际图形 Rectangle
、Square
、Circle
等等实现各自的求取面积的具体方法。至此,我们可以通过同一种调用方式,获得各种图形的面积。
如果是程序小白,可能对接口的概念还不是太熟悉,但是慢慢你会发现,接口是非常有用的设计方式。比如在现实生活中,每个医院都遵循一样的工作方式,挂号-看病-交钱-取药,我们只需要掌握这一种工作流程,去每一所医院,都不会搞的晕头转向。
让我们一步一步来,首先,创建一个 Rectangle
,并且打印其面积
package main
import "fmt"
//define a Rectangle struct that has a length and a width
type Rectangle struct {
length, width int
}
//write a function Area that can apply to a Rectangle type
func (r Rectangle) Area() int {
return r.length * r.width
}
func main() {
r := Rectangle{length:5, width:3} //define a new Rectangle instance with values for its properties
fmt.Println("Rectangle details are: ",r)
fmt.Println("Rectangle's area is: ", r.Area())
}
Rectangle details are: {5 3}
Rectangle's area is: 15
就如同前面的代码一样,上述的代码中没有接口,让我们把 Area()
拿出来,放入接口 Shaper
当中。
在
Go
中通过在命名之后添加er
以表征这是一个接口,当然其它定义也是可能,这样只是大家遵循的一种编程命名方式,可以使代码更易读。
package main
import "fmt"
//Shaper is an interface and has a single function Area that returns an int.
type Shaper interface {
Area() int
}
type Rectangle struct {
length, width int
}
//this function Area works on the type Rectangle and has the same function signature defined in the interface Shaper. Therefore, Rectangle now implements the interface Shaper.
func (r Rectangle) Area() int {
return r.length * r.width
}
func main() {
r := Rectangle{length:5, width:3}
fmt.Println("Rectangle r details are: ", r)
fmt.Println("Rectangle r's area is: ", r.Area())
s := Shaper(r)
fmt.Println("Area of the Shape r is: ", s.Area())
}
Rectangle r details are: {5 3}
Rectangle r's area is: 15
Area of the Shape r is: 15
在 C++
或者 Java
中继承接口时,需要明确的声明这一关系,例如上面的代码,用 Java
来写的话,可能为如下的语句
public class Rectangle implements Shaper { //implementation here }
在 Go
中,无需表现出这层关系。上面代码中,接口 Shaper
定义一个方式 Area() int
。而 Rectangle
也定义了一个相同的方法 func (r Rectangle) Area() int
。所以, Go
会认为 Rectangle
实现了 Shaper
的接口。所以,我们可以定义 Rectangle
为 Shaper
类型:r := Shaper(r)
,之后调用 Area()
方法,输出 Rectangle
的面积。
让我们继续添加 Square
也实现 Area() int
, 来看一看如何通过接口统一的访问每种类型的面积。
package main
import "fmt"
type Shaper interface {
Area() int
}
type Rectangle struct {
length, width int
}
func (r Rectangle) Area() int {
return r.length * r.width
}
type Square struct {
side int
}
//the Square type also has an Area function and therefore, it too, implements the Shaper interface
func (sq Square) Area() int {
return sq.side * sq.side
}
func main() {
r := Rectangle{length:5, width:3}
fmt.Println("Rectangle details are: ",r)
var s Shaper
s = r //equivalent to "s = Shaper(r)" since Go identifies r matches the Shaper interface
fmt.Println("Area of Shaper(Rectangle): ", s.Area())
fmt.Println()
q := Square{side:5}
fmt.Println("Square details are: ",q)
s = q //equivalent to "s = Shaper(q)
fmt.Println("Area of Shaper(Square): ", s.Area())
fmt.Println()
}
Rectangle details are: {5 3}
Area of Shaper(Rectangle): 15
Square details are: {5}
Area of Shaper(Square): 25
当前我们按照 Rectange
的方式实现了另一个类型 Square
。 让我们通过循环的方式,更加准确的说明一下接口的用途。在下面的代码中,创建 Shaper
实例的列表,通过循环输出每种具体类型的面积。
package main
import "fmt"
type Shaper interface {
Area() int
}
type Rectangle struct {
length, width int
}
func (r Rectangle) Area() int {
return r.length * r.width
}
type Square struct {
side int
}
func (sq Square) Area() int {
return sq.side * sq.side
}
func main() {
r := Rectangle{length:5, width:3}
q := Square{side:5}
shapesArr := [...]Shaper{r, q}
fmt.Println("Looping through shapes for area ...")
for n, _ := range shapesArr {
fmt.Println("Shape details: ", shapesArr[n])
fmt.Println("Area of this shape is: ", shapesArr[n].Area())
}
}
Looping through shapes for area ...
Shape details: {5 3}
Area of this shape is: 15
Shape details: {5}
Area of this shape is: 25
译者注:关于 Go
interface
的设 计方式,如果大家觉得很新颖,可以看看这篇文章 Duck Typing
Golang一种神奇的语言,让我们一起进步