18《Go语言入门》非侵入式接口(interface)

这是我纯手写的《Go语言入门》,手把手教你入门Go。源码+文章,看了你就会,此处应有掌声!
文章中所有的代码我都放到了github.com/GanZhiXiong/go_learning这个仓库中。在看文章的时候,对照仓库中代码学习效果更佳!

目录

  • 什么是接口
    • 接口的作用
    • 其它编程语言中的接口
  • Go的接口特征
    • 非侵入式设计
      • 好处
      • 坏处
  • 接口声明(定义)
  • 常见接口写法
  • 实现接口的条件
  • 类型与接口的关系
  • 支持

什么是接口

接口本身是调用方和实现方均需遵守的一种协议,大家按照统一的方法命名、参数类型和数量来协调逻辑处理的过程。

接口的作用

  • 上下游解耦

接口相当于调用方实现方的一个中间层,通过中间层约定的接口来协调逻辑处理,解耦了有依赖关系的上下游。

  • 隐藏底层实现
  • 减少了代码的关注点

其它编程语言中的接口

接口是一种较为常见的特性,很多语言都有接口特性。Java、C/C++、C# 语言中的接口都可以多重派生实现接口组合;在苹果的 Objective C 中与接口类似的功能被称为 Protocol,这种叫法比接口更形象、具体。

Go的接口特征

Go语言不是一种 “传统” 的面向对象编程语言:它里面没有类和继承的概念。

但是Go语言里有非常灵活的接口概念,通过它可以实现很多面向对象的特性。

Go中的接口定义了一组方法的签名。

非侵入式设计

Go语言的接口实现是隐式的,无须让实现接口的类型写出实现了哪些接口,直接实现接口中的方法即可。这个设计被称为非侵入式设计

与之相反的叫侵入式设计:实现类需要明确声明自己实现了某个接口(如Java:class Foo implements IFoo)。

Go的接口设计是非侵入式的,非侵入式体现为:

  • 接口编写者无须知道接口被哪些类型实现。
  • 而接口实现着只需要知道的是什么样子的接口,而无须指明现实哪一个接口。
  • 编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。

传统的派生式接口及类关系构建的模式,让类型间拥有强耦合的父子关系。这种关系一般会以“类派生图”的方式进行。经常可以看到大型软件极为复杂的派生树。随着系统的功能不断增加,这棵“派生树”会变得越来越复杂。

好处

非侵入式设计是 Go 语言设计师经过多年的大项目经验总结出来的设计之道。

  • 不用为了实现一个接口而导入一个包了。想实现一个接口,直接实现它包含的方法就好了。
  • 不用在写类型的时候先想怎么设计接口的问题,直接把需要提供的方法全写了就成。至于哪个方法是哪个接口的,不用太操心。
  • 为已经存在的具体类型创建一个新的接口时,不会去改变这些类型的定义,当我们使用的类型来自于不受我们控制的包时这种设计尤其有用。
  • 让接口和实现者真正解耦,编译速度才能真正提高,项目之间的耦合度也会降低不少。

坏处

  • 当代码量大的时候,你可能很难知道实现了哪一个接口,使代码阅读变得复杂了

接口声明(定义)

接口类型是由数个方法组成。

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2}
  • 接口类型名:使用 type 将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加 er,如有写操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有关闭功能的接口叫 Closer 等。
  • 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略,例如:
type writer interface{
    Write([]byte) error
}

常见接口写法

Go语言提供的很多包中都有接口,例如 io 包中提供的 Writer 接口:

type Writer interface {
    Write(p []byte) (n int, err error)
}

类似的,还有将一个对象以字符串形式展现的接口,只要实现了这个接口的类型,在调用 String() 方法时,都可以获得对象对应的字符串。在 fmt 包中定义如下:

type Stringer interface {
    String() string
}

Stringer 接口在Go语言中的使用频率非常高,功能类似于 Java 或者 C# 语言里的 ToString 的操作。

实现接口的条件

  • 接口的方法与实现接口的类型方法格式一致

在类型中添加与接口签名一致的方法就可以实现该方法。签名包括方法中的名称、参数列表、返回参数列表。

  • 接口中所有方法均需要被实现

以上两个条件,只要一个不满足就会编译报错。

下面用代码演示接口的实现:

// 定义DataWriter接口
type DataWriter interface {
	WriteData(data interface{}) error
	// 能否写入
	CanWrite() bool
}

// 定义文件结构,用于实现DataWriter
type file struct {
}

func (f *file) CanWrite() bool {
	panic("implement me")
}

// 实现DataWriter接口的WriteData方法
// 有些人可能会觉得,下面重复写接口的方法名、参数和返回值很麻烦
// 其实一般IDE都带有快速实现自动生成的功能,比如GoLand在定义结构体的类型名上按下Ctrl+I即可快速实现接口
//func (f *file) WriteDataX(data interface{}) error {
func (f *file) WriteData(data interface{}) error {
	fmt.Println("WriteData: ", data)
	return nil
}

func TestInterface(t *testing.T) {
	f := new(file)
	var writer DataWriter
	writer = f
	writer.WriteData("data")
}

类型与接口的关系

  • 一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。

例如:

type Socket struct {
}

// Socket 结构的 Write() 方法实现了 io.Writer 接口:
func (s *Socket) Write(p []byte) (n int, err error) {
	return 0, nil
}

// Socket 结构也实现了 io.Closer 接口:
func (s *Socket) Close() error {
	return nil
}

// 使用io.Writer的代码, 并不知道Socket和io.Closer的存在
func usingWriter( writer io.Writer){
	writer.Write( nil )
}
// 使用io.Closer, 并不知道Socket和io.Writer的存在
func usingCloser( closer io.Closer) {
	closer.Close()
}

func TestInterfaceTypeRelations(t *testing.T) {
	s := new(Socket)
	// usingWriter() 和 usingCloser() 完全独立,互相不知道对方的存在,
	// 也不知道自己使用的接口是 Socket 实现的。
	usingWriter(s)
	usingCloser(s)
	t.Log(s)
}
  • 多个类型可以实现相同的接口

例如下面示例选择将Logger 嵌入到 GameService 能最大程度地避免代码冗余,简化代码结构。

/*
多个类型可以实现相同的接口
*/
type Service interface {
	Start()  // 启动服务
	Log(string) // 日志输出
}

// 日志器
type Logger struct {
}

// 实现Service接口的Log方法
func (l *Logger) Log(s string) {
	fmt.Println(s)
}

// 游戏服务
type GameService struct {
	// 选择将 Logger 嵌入到 GameService 能最大程度地避免代码冗余,简化代码结构。
	Logger
}

func (g *GameService) Start() {
	fmt.Println("启动服务成功")
}

func TestGameService(t *testing.T) {
	var s Service = new(GameService)
	s.Start()
	s.Log("hello")
}

支持

  • 我会持续编写【软件开发相关】的文章,保持每周至少一篇文章。
  • 如果你也是【软件工程师】,【关注❤️我】,一定会对你有所帮助。
  • 如果这篇文章对你有所帮助,那就麻烦,【点赞】
  • 您的支持将给与我更大的动力,谢谢。

你可能感兴趣的:(#,Go语言入门,go,golang,go语言)