本教材由知了传课辛苦制作而成,仅供学习使用,请勿用于商业用途!如进行转载请务必注明出处!谢谢!

接口

在讲解具体的接口之前,先看如下问题。

使用面向对象的方式,设计一个加减的计算器

代码如下:

package main import "fmt" //父类,这是结构体 type Operate struct { num1 int num2 int } //加法子类,这是结构体 type Add struct { Operate } //减法子类,这是结构体 type Sub struct { Operate } //加法子类的方法 func (a *Add) Result() int { return a.num1 + a.num2 } 可以看到ADD里面是用父类结构体的,然后直接返回num1+num2就行了 //减法子类的方法 func (s *Sub) Result() int { return s.num1 - s.num2 } 可以看到Sub里面是用父类结构体的,然后直接返回num1-num2就行了 //方法调用 func main0201() { //创建加法对象 //var a Add //a.num1 = 10 //a.num2 = 20 //v := a.Result() //fmt.Println(v) //可以看到调用起来还是很简单的,直接给父类结构体的属性赋值,然后调用加法的方法就行。 //创建减法对象 var s Sub s.num1 = 10 s.num2 = 20 v := s.Result() fmt.Println(v) } //可以看到调用起来还是很简单的,直接给父类结构体的属性赋值,然后调用减法的方法就行

以上实现非常简单,但是有个问题,在main()函数中,当我们想使用减法操作时,创建减法类的对象,调用其对应的减法的方法。但是,有一天,系统需求发生了变化,要求使用加法,不再使用减法,那么需要对main()函数中的代码,做大量的修改。将原有的代码注释掉,创建加法的类对象,调用其对应的加法的方法。有没有一种方法,让main()函数,只修改很少的代码就可以解决该问题呢?有,要用到接下来给大家讲解的接口的知识点。

一、 什么是接口

接口就是一种规范与标准,在生活中经常见接口,例如:笔记本电脑的USB接口,可以将任何厂商生产的鼠标与键盘,与电脑进行链接。为什么呢?原因就是,USB接口将规范和标准制定好后,各个生产厂商可以按照该标准生产鼠标和键盘就可以了。

在程序开发中,接口只是规定了要做哪些事情,干什么。具体怎么做,接口是不管的。这和生活中接口的案例也很相似,例如:USB接口,只是规定了标准,但是不关心具体鼠标与键盘是怎样按照标准生产的.

在企业开发中,如果一个项目比较庞大,那么就需要一个能理清所有业务的架构师来定义一些主要的接口,这些接口告诉开发人员你需要实现那些功能。

二、 接口定义

接口定义的语法如下:

//先定义接口 一般以er结尾 根据接口实现功能 type Humaner interface { //方法 方法的声明 sayhi() }

怎样具体实现接口中定义的方法呢?

//Student的结构体 type student11 struct { name string age int score int } //Student的打印方法 func (s *student11)sayhi() { fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n",s.name,s.age,s.score) } //teacher11的结构体 type teacher11 struct { name string age int subject string } //teacher11的方法 func (t *teacher11)sayhi() { fmt.Printf("大家好,我是%s,今年%d岁,我的学科是%s\n",t.name,t.age,t.subject) }

具体的调用如下:

func main() { //接口是一种数据类型 可以接收满足对象的信息 //接口是虚的 方法是实的 //接口定义规则 方法实现规则 //接口定义的规则 在方法中必须有定义的实现 var h Humaner stu := student11{"小明",18,98} //stu.sayhi() //将对象信息赋值给接口类型变量 h = &stu h.sayhi() //直接将Student的对象赋值给了h接口,然后就能实现方法的调用 tea := teacher11{"老王",28,"物理"} //tea.sayhi() //将对象赋值给接口 必须满足接口中的方法的声明格式 h = &tea h.sayhi() }

只要类(结构体)实现对应的接口,那么根据该类创建的对象,可以赋值给对应的接口类型。

接口的命名习惯以er结尾。

三、 多态

接口有什么好处呢?实现多态。

多态就是同一个接口,使用不同的实例而执行不同操作

所谓多态指的是多种表现形式,如下图所示:

import13.png

使用接口实现多态的方式如下:

package main import "fmt" //先定义接口 一般以er结尾 根据接口实现功能 type Humaner1 interface { //方法 方法的声明 sayhi() } //student12的结构体 type student12 struct { name string age int score int } //student12的方法 func (s *student12)sayhi() { fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n",s.name,s.age,s.score) } //teacher12的结构体 type teacher12 struct { name string age int subject string } //teacher12的方法 func (t *teacher12)sayhi() { fmt.Printf("大家好,我是%s,今年%d岁,我的学科是%s\n",t.name,t.age,t.subject) } //多态的实现 //将接口作为函数参数 实现多态 func sayhello(h Humaner1) { h.sayhi() } func main() { stu := student12{"小明",18,98} //调用多态函数 sayhello(&stu) tea := teacher12{"老王",28,"Go"} sayhello(&tea) }

关于接口的定义,以及使用接口实现多态,大家都比较熟悉了,但是多态有什么好处呢?现在还是以开始提出的计算器案例给大家讲解一下。

四、多态案例

使用多态的功能,实现一个加减计算器。完整代码如下:

package main import "fmt" //定义接口 type Opter interface { //方法声明 Result() int } //父类 type Operate struct { num1 int num2 int } //加法子类 type Add struct { Operate } //加法子类的方法 func (a *Add) Result() int { return a.num1 + a.num2 } //减法子类 type Sub struct { Operate } //减法子类的方法 func (s *Sub) Result() int { return s.num1 - s.num2 } //创建一个类负责对象创建 //工厂类 type Factory struct { } func (f *Factory) Result(num1 int, num2 int, ch string) { switch ch { case "+": var a Add a.num1 = num1 a.num2 = num2 Result(&a) case "-": var s Sub s.num1 = num1 s.num2 = num2 Result(&s) } } //通过设计模式调用 func main() { //创建工厂对象 var f Factory f.Result(10, 20, "+") }

四、 接口继承与转换(了解)

接口也可以实现继承:

package main import "fmt" //先定义接口 一般以er结尾 根据接口实现功能 type Humaner2 interface { //子集 //方法 方法的声明 sayhi() } type Personer interface { //超集 Humaner2 //继承sayhi() sing(string) } type student13 struct { name string age int score int } func (s *student13)sayhi() { fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n",s.name,s.age,s.score) } func (s *student13)sing(name string) { fmt.Println("我为大家唱首歌",name) } func main() { //接口类型变量定义 var h Humaner2 var stu student13 = student13{"小吴",18,59} h = &stu h.sayhi() //接口类型变量定义 var p Personer p = &stu p.sayhi() p.sing("大碗面") }

接口继承后,可以实现“超集”接口转换“子集”接口,代码如下:

package main import "fmt" //先定义接口 一般以er结尾 根据接口实现功能 type Humaner2 interface { //子集 //方法 方法的声明 sayhi() } type Personer interface { //超集 Humaner2 //继承sayhi() sing(string) } type student13 struct { name string age int score int } func (s *student13)sayhi() { fmt.Printf("大家好,我是%s,今年%d岁,我的成绩%d分\n",s.name,s.age,s.score) } func (s *student13)sing(name string) { fmt.Println("我为大家唱首歌",name) } func main() { //接口类型变量定义 var h Humaner2 //子集 var p Personer //超集 var stu student13 = student13{"小吴",18,59} p = &stu //将一个接口赋值给另一个接口 //超集中包含所有子集的方法 h = p //ok h.sayhi() //子集不包含超集 //不能将子集赋值给超集 //p = h //err //p.sayhi() //p.sing("大碗面") }

五、 空接口

空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。例如:

var i interface{} //接口类型可以接收任意类型的数据 //fmt.Println(i) fmt.Printf("%T\n",i) i = 10 fmt.Println(i) fmt.Printf("%T\n",i)

当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标准库fmt中PrintXXX系列的函数,例如:

func Printf(fmt string, args ...interface{}) func Println(args ...interface{})

如果自己定义函数,可以如下:

func Test(arg ...interface{}) { }

Test()函数可以接收任意个数,任意类型的参数。

1550人已阅读,今天你学习了吗?

添加新回复