非零基础自学Golang
文章目录
- 非零基础自学Golang
- 第12章 接口与类型
- 12.6 小结
- 12.7 知识拓展
- 12.7.1 非侵入式接口
第12章 接口与类型
12.6 小结
- 了解什么是鸭子类型。
- 掌握接口的定义与实现。
- 了解什么是接口嵌入。
- 掌握空接口的常见用法,如空接口的赋值操作。
- 掌握类型断言的两种方式:ok-pattern和switch-type。
- Go语言的接口是非常易用、简洁的,需要重点掌握的是Go语言的接口实现和类型断言,能够判断接口值是什么类型,接口值的内容是什么。
12.7 知识拓展
12.7.1 非侵入式接口
Go语言的接口是非侵入式的,我们先来看侵入式接口与非侵入式接口的区别:
- 侵入式接口:需要显式地创建一个类去实现一个接口。
- 非侵入式接口:不需要显式地创建一个类去实现一个接口。
大部分语言的接口都是侵入式接口,需要显式地去创建声明实现一个接口。
例如Java的接口定义如下,要实现一个接口,必须使用implements显式声明。
[动手写 12.7.1]
package Java20221205;
/**
* ClassName: IFoo
* date: 2022/12/5 15:48
*
* @author DingJiaxiong
*/
public interface IFoo {
void Bar();
}
package Java20221205;
/**
* ClassName: Foo
* date: 2022/12/5 15:48
*
* @author DingJiaxiong
*/
class Foo implements IFoo {
@Override
public void Bar() {
}
}
非侵入式接口有三个好处:
- 去掉了繁杂的继承体系,Go语言的标准库再也不需要绘制类库的继承树图。在Go中,类的继承树并无意义,你只需要知道这个类实现了哪些方法、每个方法是何含义就足够了。
- 实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。
- 不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。
接口由使用方按自身需求来定义,使用方无须关心是否有其他模块定义过类似的接口。
总的来说,非入侵式的接口设计更简洁、灵活,更注重实用性。
下面我们从代码层面来更深入地理解。
[ 动手写12.7.2 ]
package main
type IPhoner interface {
Call() error
Video() error
Game() error
}
type Apple interface {
Call() error
Video() error
}
type Huawei interface {
Call() error
Game() error
}
动手写12.7.2创建了三个接口,分别是IPhoner、Apple、Huawei,其中Apple、Huawei接口的方法在IPhoner接口中全部包含。
我们再来创建一个实现IPhoner接口的结构体:
[ 动手写12.7.3]
package main
import "fmt"
type IPhoner interface {
Call() error
Video() error
Game() error
}
type Apple interface {
Call() error
Video() error
}
type Huawei interface {
Call() error
Game() error
}
type Phone struct {
Name string
}
func (p *Phone) Call() error {
fmt.Println(p.Name, "Start Call")
return nil
}
func (p *Phone) Video() error {
fmt.Println(p.Name, "Start Video")
return nil
}
func (p *Phone) Game() error {
fmt.Println(p.Name, "Start Game")
return nil
}
动手写12.7.3创建了一个叫Phone的结构体,并有实现IPhoner的三个方法Call()、Video()、Game(),也就是说,Phone是IPhoner这个接口的一个实现。
这时,如果我们想要获得一个Apple的对象,在传统的侵入式接口语言里,至少要写一个appleClass类去实现Apple接口,而为了得到一个Huawei的对象又不得不写一个huaweiClass类去实现Huawei接口。同理,要得到一个IPhoner的对象也得这么做,但显然这样的操作,代码的复用率并不高。
而在Go语言中,并不需要这样,只需要写一个通用的IPhoner接口实现就可以完成以上需求,也就是只需要Phone这个实现就可以了。
[ 动手写 12.7.4 ]
package main
import "fmt"
type IPhoner interface {
Call() error
Video() error
Game() error
}
type Apple interface {
Call() error
Video() error
}
type Huawei interface {
Call() error
Game() error
}
type Phone struct {
Name string
}
func (p *Phone) Call() error {
fmt.Println(p.Name, "Start Call")
return nil
}
func (p *Phone) Video() error {
fmt.Println(p.Name, "Start Video")
return nil
}
func (p *Phone) Game() error {
fmt.Println(p.Name, "Start Game")
return nil
}
func main() {
var apple Apple = &Phone{"apple"}
var huawei Huawei = &Phone{"huawei"}
apple.Call()
huawei.Game()
}
运行结果
动手写12.7.4仅使用Phone这个接口实现就完成了Apple和Huawei这两个接口的实例。
动手写12.7.4也可以使用new来生成Apple的实例:
[ 动手写 12.7.5]
package main
import "fmt"
type IPhoner interface {
Call() error
Video() error
Game() error
}
type Apple interface {
Call() error
Video() error
}
type Huawei interface {
Call() error
Game() error
}
type Phone struct {
Name string
}
func (p *Phone) Call() error {
fmt.Println(p.Name, "Start Call")
return nil
}
func (p *Phone) Video() error {
fmt.Println(p.Name, "Start Video")
return nil
}
func (p *Phone) Game() error {
fmt.Println(p.Name, "Start Game")
return nil
}
func main() {
var apple Apple = new(Phone)
var huawei Huawei = new(Phone)
apple.Call()
huawei.Game()
}
运行结果
没毛病,懂了!!!!