Go 使用接口作为参数创建另一个包的等效接口

Go 使用接口作为参数创建另一个包的等效接口,go,interface,Go,Interface,我正在练习编写惯用的Go代码,发现接口应该在使用它们的包中声明,因为它们是隐式的。然而,我遇到了这种情况,在第二个包(包b)中,我希望一个函数调用包a中结构的接收方函数,而不紧密耦合它 因此,很自然,我在包b中声明了一个接口,该接口带有我想从包a调用的函数的签名。问题是这个函数接受一个特定类型的参数,该参数是包a中声明的接口。因为我不希望包b导入包a,所以我在包b中定义了一个接口,该接口的签名与包a中的签名完全相同。下面的游乐场链接显示了示例代码 此代码不可编译。所以这里的问题是,我是否错误地

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

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

此代码不可编译。所以这里的问题是,我是否错误地使用了接口,或者应该在单独的包中定义具体的类型,或者最后,对于我试图解决的问题,是否有更好的代码模式

编辑:为了澄清,包a和包b不相互导入。main()代码存在于一个单独的包中,该包将这两个代码连接起来。

解决方案 有一个类型在两个包a和B中使用。包a导入包B

您必须避免循环依赖,因此有三种选择:

  • 在包B中定义类型,使其在两个包中都可用 包裹
  • 在包C中定义类型,并让A和B导入包C
  • 更改设计,使A和B中都不使用该类型
  • 这些是您的选择,无论该类型是接口还是任何其他类型

    选择最适合您的设计目标的选项

    规则/习语 我正在练习编写惯用的Go代码,并发现 接口应该在使用它们的包中声明 因为它们是含蓄的

    我有这种冲动/想法——问题是它不太实用。如果接口只能在定义它们的包中使用,那么它们就没有那么有用了。标准库中充满了违反这一公理的代码。因此,我不认为所提出的规则——适用于所有情况、设计和上下文中的所有接口——是惯用的


    例如,看看这个包。使用另一个包中定义的接口
    io.Reader
    。使用另一个包中定义的接口
    io.Writer

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

    像这样的:()

    编译错误:
    /g.go:8:26:无法将f1(类型func(I1))转换为类型func(接口{})

    答案似乎是否定的,因为GO没有考虑<代码> FUNC(I1)< /代码>。 相当于

    func(接口{})
    。政府是这样说的

    函数类型表示具有相同参数和结果类型的所有函数的集合

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

    回到你原来的问题:

    我正在练习编写惯用的Go代码,发现接口 应该在使用它们的包中声明,因为它们是 含蓄的

    是的,这个想法很好,但它不适用于您的实现方式 我想是的。因为您希望有不同的
    runnerRegisterer
    并且它们都必须具有具有相同签名的方法 使用
    Runner
    界面,可以在公共文档中定义
    Runner
    地点。此外,如上所述,Go不允许您使用不同的接口 在方法中,无论如何都要签名

    根据我对你想要达到的目标的理解,以下是我的想法 您应该重新安排代码:

  • runnerristerer
    (注意:这是公共的)和
    Runner
    定义在一个 包裹
  • 在同一个包中实现您的
    RunnerCoach
    ,并使用上述 接口。您的
    RunnerCoach
    使用实现接口的类型, 所以它定义了它们
  • 在另一个包中实现您的跑步者。您没有定义
    Runner
    接口在这里
  • 在另一个使用接口的包中实现您的
    管理器
    RunnerCoach
    的包中定义了
    Runner
    ,因为它必须采用该类型 如果要用作
    RunnerRegisterer
    ,则作为参数

  • 但它不会编译,不是因为循环依赖关系。在上面的示例中,包A和包B不相互导入。其目的是main()(称为package M)代码通过接口将两个包连接在一起。问题是RunnerB(pkg b)接口未检测到与Runner(pkg a)接口等效,尽管它们定义了相同的函数签名,因此类型检查失败。是的,我知道。我尝试了你的游乐场代码,并在两个包中使用相同的接口类型对其进行了调整。如果我不理解这个问题,我就不会这样回答。经过编辑的答案更清楚,并且提供了相关的参考,说明为什么它不是一个一致的公理。谢谢
    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()
    }
    
    package main
    
    type I1 interface{}
    
    func f1(x I1) {}
    
    func main() {
        f := (func(interface{}))(f1)
        f(nil)
    }