杂谈:正确使用Go接口

杂谈:正确使用Go接口

https://blog.chewxy.com/2018/03/18/golang-interfaces/

不要写Java风格的接口

由于Go实现一个接口只需要含有签名完全一致(包括receiver、args和retval)就行了,而不像Java中那样通过 implements 关键字来指定实现的接口类型,所以Go中不要写这样的代码:

package animals 

type Animal interface {
	Speaks() string
}

// implementation of Animal
type Dog struct{}
func (a Dog) Speaks() string { return "woof" }
package circus

import "animals"

func Perform(a animal.Animal) string { return a.Speaks() }

应该修正:

  • Animal 改名为 Speaker ,缩小接口的语义
  • 不要Animal在包中定义animals,而是在使用点——包 circus 中定义

Postel定律

一般来说,函数接收接口,返回结构体

func funcName(INTERFACETYPE) CONCRETETYPE

这不仅是通过接口来约束了 funcName 中的行为(不会进行除了接口定义的方法之外的操作,比如访问成员变量),同时也方便测试Mock——只需要传入一个Mock了接口的对象即可。

适度预定义接口

编程的形式相当自由——没有什么硬性规定。你当然可以预先定义一个接口。

预先定义接口通常会导致代码过度设计。但在某些情况下,你显然需要预先定义接口。我能想到几种情况:

  • 密封接口(Sealed Interfaces)
  • 抽象数据类型(Abstract Data Types)
  • 递归接口(Recursive Interfaces)

密封接口(Sealed Interfaces)

密封接口是指具有未导出方法的接口。这意味着包外的用户无法创建实现该接口的类型。

package bar
type Fooer interface {
	Foo() 
	sealed()
}

这样一来只有在 bar 包内才可以使用和创建 Fooer 类型的对象。

抽象数据类型(Abstract Data Types)

就是接口本来的用途。比如sort包:

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

如果你想使用这个sort包,你就必须实现接口的这3个方法

递归接口(Recursive Interfaces)

是一种“自举”的接口:

type Fooer interface {
	Foo() Fooer
}

This pattern is useful for creating contexts to operate in.

posted @ 2025-05-05 22:50  3的4次方  阅读(11)  评论(0)    收藏  举报