go - 使用接口(interface)作为参数创建另一个包的等效接口(interface)

标签 go interface

我正在练习编写惯用的 Go 代码并发现接口(interface)应该在使用它们的包中声明,因为它们是隐式的。但是我遇到了这种情况,在第二个包(包 b)中,我想要一个函数来调用包 a 中的结构的接收函数,而不紧密耦合它。

很自然地,我在包 b 中声明了一个接口(interface),带有我想从包 a 调用的函数的签名。问题是这个函数接受一个特定类型的参数,这个参数是在包 a 中声明的接口(interface)。因为我不希望包 b 导入包 a,所以我在包 b 中定义了一个接口(interface),其签名与包 a 中存在的签名完全相同。下面的 playground 链接显示了示例代码。

Playground

package main

import (
    "fmt"
    "log"
)

func main() {
    manager := &Manager{}
    coach := NewRunnerCoach(manager)
    fmt.Println("Done")
}

// package a

type Runner interface {
    Run()
}

type Manager struct {
}

func (o *Manager) RegisterRunner(runner Runner) {
    log.Print("RegisterRunner")
}

func (o *Manager) Start() {
    log.Print("Start")
}

// package b

type RunnerCoach struct {
    runner *FastRunner
}

func NewRunnerCoach(registerer runnerRegisterer) *RunnerCoach {
    runnerCoach := &RunnerCoach{&FastRunner{}}
    registerer.RegisterRunner(runnerCoach.runner)
    return runnerCoach
}

type FastRunner struct {
}

func (r *FastRunner) Run() {
    log.Print("FastRunner Run")
}

// define ther registerer interface coach is accepting
type runnerRegisterer interface {
    RegisterRunner(runner RunnerB)
}

// declaring a new interface with the same signature because we dont want to import package a
// and import Runner interface
type RunnerB interface {
    Run()
}

此代码无法编译。所以这里的问题是,我是不是错误地使用了接口(interface),或者应该在单独的包中定义具体类型,或者最后,是否有更好的代码模式来解决我要解决的问题?

编辑:澄清一下,包 a 和 b 不会相互导入。 main() 代码存在于连接这两者的单独包中。

最佳答案

IIUC,你的问题不是关于包,而是归结为一个函数(或方法)是否 可以类型转换为另一个接受等效参数的函数,但是 不同的接口(interface)类型。

像这样:(Go Playground)

package main

type I1 interface{}

func f1(x I1) {}

func main() {
    f := (func(interface{}))(f1)
    f(nil)
}

编译错误:./g.go:8:26: cannot convert f1 (type func(I1)) to type func(interface {})

答案似乎是否定的,因为 Go 不认为 func (I1) 是 相当于 func (interface{})Go spec说这个

A function type denotes the set of all functions with the same parameter and result types.

func (I1)func (interface{}) 类型不采用相同的参数,即使 I1 被定义为 interface{}。您的代码无法编译为类似 原因是 func (runner RunnerB)func (runner Runner) 不同,因此 *Manager 的方法集不是超集的 接口(interface)runnerRegisterer

回到你原来的问题:

I am practicing writing idiomatic Go code and discovered that interfaces should be declared in packages which are consuming them since they're implicit.

是的,这个想法很好,但它不适用于您的实现方式 认为是的。因为你期望有不同的实现 runnerRegisterer 并且它们都必须有一个具有相同签名的方法 使用 Runner 接口(interface),定义 Runner 是有意义的 地方。此外,如上所示,Go 不允许您使用不同的接口(interface) 无论如何在方法签名中。

根据我对您要实现的目标的理解,这是我的想法 你应该重新安排你的代码:

  1. 定义RunnerRegisterer(注意:这是公开的)和Runner合二为一 包。
  2. 在同一个包中实现你的 RunnerCoach 并使用上面的 接口(interface)。您的 RunnerCoach 使用实现接口(interface)的类型, 所以它定义了它们。
  3. 在另一个包中实现您的运行者。您没有定义 Runner 界面在这里。
  4. 在另一个使用该接口(interface)的包中实现您的 Manager RunnerRunnerCoach 的包中定义,因为它必须采用该类型 作为参数,如果它想用作 RunnerRegisterer

关于go - 使用接口(interface)作为参数创建另一个包的等效接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52349661/

相关文章:

azure - 使用 Go Task 的 Yaml Azure 管道安装 Terratest 时出现问题 - 在子文件夹中触发 terratest 测试时出现问题

go - 返回响应后关闭 HTTP 服务器

Java接口(interface)问题

c++ - 为什么 std::map::insert 不能采用键和值而不是 std::pair?

go - pq QueryRow 扫描失败,内存地址无效或指针取消引用

go - Go中AES-GCM + Base64后无法解密

go - `Listen` 和 `ListenTCP` 之间有什么不同?

java - 登录和未登录用户的不同 UI 功能

programming-languages - Haskell 的类型类和 Go 的接口(interface)

java - 为什么要重写接口(interface)方法?