Golang进阶

go防止接口被无意间实现

2021-12-09  本文已影响0人  qishuai

go语言接口的独特之处在于它是满足隐式实现的鸭子模式,如果走起路来像鸭子,叫声也像鸭子,那么就会被认为是鸭子。
在go语言实现某个接口,并不需要显式声明,只要实现了接口声明的方法集合,就自动实现了该接口。但如果我们并不想让外部的包去实现我们的接口,只是作为了一种类型数据供调用端使用;或者我们想刻意的区分某一种类型和已经存在的某种具有相同函数集合的类型,因此需要对方法集合做一定的修改。

runtime包里面就有类似需求,将runtime.Error和builtin error进行区分

// The Error interface identifies a run time error.
type Error interface {
    error

    // RuntimeError is a no-op function but
    // serves to distinguish types that are run time
    // errors from ordinary errors: a type is a
    // run time error if it has a RuntimeError method.
    RuntimeError()
}

testing.TB也有类似设计:

// TB is the interface common to T and B.
type TB interface {
    Cleanup(func())
    Error(args ...interface{})
    Errorf(format string, args ...interface{})
    Fail()
    FailNow()
    Failed() bool
    Fatal(args ...interface{})
    Fatalf(format string, args ...interface{})
    Helper()
    Log(args ...interface{})
    Logf(format string, args ...interface{})
    Name() string
    Setenv(key, value string)
    Skip(args ...interface{})
    SkipNow()
    Skipf(format string, args ...interface{})
    Skipped() bool
    TempDir() string

    // A private method to prevent users implementing the
    // interface and so future additions to it will not
    // violate Go 1 compatibility.
    private()
}

上面的两种方式,主要让大家知道为什么会有这些特殊的场景,看到类似代码能够知道他的意图。
下面我们举一个例子,比如一个包中定义了一个接口,接口里面包含了一个私有方法(函数首字母小写)

package packagea

type MyStringer interface {
    String() string

    private()
}

我们我们想在另一个包中实现这个接口,代码如下:

package main

import (
    "github.com/qshuai/packagea"
)

type User struct {}

func (u *User) Error() string {
    return "error interface"
}

func (u *User) String() string {
    return "hello world"
}


func (u *User) private() {
    panic("implement me")
}

func main() {
    var _ error = (*User)(nil)
    var _ packagea.MyStringer = (*User)(nil)  // 这里会报错:cannot use (*User)(nil) (type *User) as type packagea.MyStringer in assignment:
        *User does not implement packagea.MyStringer (missing packagea.private method)
                have private()
                want packagea.private()

}

不过我们通过下面的方式变相实现上面定义的接口:

package main

import (
    "fmt"

    "github.com/qshuai/test/packagea"
)

type User struct {
    packagea.MyStringer
}

func (u *User) Error() string {
    return "error interface"
}

func (u *User) String() string {
    return "hello world"
}

//func (u *User) private() {
//  panic("implement me")
//}

func main() {
    var _ error = (*User)(nil)
    var _ packagea.MyStringer = (*User)(nil)

    var pi packagea.MyStringer = (*User)(nil)
    fmt.Println(pi.String())
}
上一篇下一篇

猜你喜欢

热点阅读