【问题标题】:golang interface that depends on interface依赖接口的golang接口
【发布时间】:2016-08-23 18:08:16
【问题描述】:

我不确定这些应该是两个单独的问题还是一个,但在我看来,这是一个由两个部分组成的问题 - 应该如何使用 go 接口?我有这两个挣扎:

接口的方法是全局范围的:如果我有接口 A 和接口 B,它们都实现了相同的方法 Foo,但具有不同的参数或返回类型,我无法从同一类型同时实现两者。例如,我在一个返回类型为 []byte 的接口和另一个 ([]byte, error) 接口中有 GetBytes() 方法,我应该如何克服这个问题?

我遇到的另一个问题是,当我尝试定义接口时说接口 A 具有返回在同一层中定义的接口 B 的方法。现在,如果我想创建一个实现 A 的对象,如果我返回实现 B 的结构,则 go 不够聪明,无法推断该方法实现了 A 中的方法,它迫使我创建对 B 的依赖。这似乎完全击败了首先说明接口工作的方式。我怎样才能避免这个问题?

例如,如果我有:

type B interface {
    Bar()
}

type A interface {
    Foo() B
}

对于以下结构:

type b_impl struct{}

func (b b_impl) Bar() {}

type a_impl struct{}

一个 foo 方法

func (a a_impl) Foo() b_impl {}

不满足接口A,需要做:

func (a a_impl) Foo() B {}

它对声明 B 的包产生依赖关系。

【问题讨论】:

  • “我应该如何克服这个问题?”你不能,因为 Go 不支持方法重载。见golang.org/doc/faq#overloading。我不太了解您的第二个问题,无法尝试回答。也许提供一个代码示例会有所帮助。
  • 感谢@smarx - 我编辑了问题,并为第二部分提供了更多详细信息
  • 我不认为你可以对你的第二个问题做任何事情。
  • 对于您的第二个“问题”:问题吗?如果方法 Foo 返回一个 Barer,它返回一个 Barer 而不是 b_impl。
  • @gsf,我一遍又一遍地阅读你的最后评论,但我无法理解你想说什么。接口是一种只定义方法集的类型,但它们仍然只是一种类型。如果在一个包中声明B,在另一个包中引用B,那么当然需要导入B的包。为什么这是个问题? io 包经常被导入以使用io.Readerio.Writer

标签: go interface


【解决方案1】:

第一个问题: 在 go 中,当您想要执行不同的任务时,您需要创建不同的函数名称。让我们看看 strconv 包中的标准库是如何解决的:https://golang.org/pkg/strconv/#pkg-index

查看 append 函数的不同声明。每种不同类型都有功能。

因此,如果您希望使用 FooInt 函数,您的界面也应该是 FooInter,...

第二个问题:举个小例子。当你想使用 io.Writer 接口时,你不需要导入整个 io 包。将 Writer 声明复制到您自己的包中是完全可以的。如果你这样做正确,每个 io.Writer 实现都会自动实现你自己的 Writer 接口。

看完其他的cmet你可能有不一样的情况:

假设有一个包 a 和 b 具有接口 a.A 和 b.B。如果有以下情况:

type A interface{
  Foo() b.B
}

你必须为a.A编写一个实现,然后你需要导入包b。但这不会让你的二进制文件变大,因为你总是需要导入包 a,这取决于 b。

【讨论】:

  • 不,根本不是理论上的。这是我实际遇到的两个问题。我“需要”改变是个好建议,但由于我不是我所依赖的一切的所有者——“我”无法改变它。对于第二个问题,io.Writer 示例与我的情况无关。几乎每个不像 io.Writer 那样明显不重要的接口都需要多个接口。由于我不是在编写隐式获取依赖项的标准库 - 这个问题非常现实。
  • 对不起,如果这听起来有点难。这应该不是本意,英语不是我的第一语言。但是,当您拥有现有代码时,了解更多有关现有结构以及您需要实现哪些部分的信息会有所帮助。而且我知道您的问题不在于 io.Writer 只是一个示例。阅读所有其他 cmets 后,我了解您的问题。如果您的外部包 a 使用外部接口 b.B 则没有其他方法可以解决此问题,然后导入包 b。
  • 我有一个包含多个接口的协议——正如解释的那样。 A 返回 B。它们都属于同一个包。现在正因为如此,我不可能在不直接依赖这个包的情况下创建这些接口的实现。如果我没有,这将是可能的 - 这就是问题所在。
  • @gsf:我明白你的意思,但它并不是真正的 Go 东西。如果您必须使用的包不是惯用的 Go 代码或非常糟糕的 Go 代码,那么使用它或寻找替代解决方案可能不是一个好主意。我知道在这种情况下这并非总是可行的,也许一种适配器可以工作。通过这种方式,您可以在包中编写好的 Go 代码,在适配器包中编写不好的代码。您的适配器将取决于包 a 和 b,但在其中您应该定义可以在代码中实现的接口。
【解决方案2】:

为了实现一个接口,你需要有完全相同的方法名和签名。表示签名必须具有相同的类型

尽管看起来很奇怪,但这两个签名并不相同:

Foo() B
Foo() b_impl {}

b_impl 实现 B 没关系。

为了克服需要导入包B 声明的问题,您可以使用匿名类型。这样编译器就不需要方法签名的特定类型。

type A interface {
    Foo() interface{Bar()}
}

也可以写成嵌入B

type A interface {
    Foo() interface{B}
}

不过,a_impl 必须具有相同的签名,所以它还需要返回一个匿名接口。也可以在a_impl的包中声明同样的B接口,并将其嵌入到匿名接口中。

package impl

type B interface {
    Bar()
}

type b_impl struct{}

func (b b_impl) Bar() {}

type a_impl struct{}

func (a a_impl) Foo() interface{B} {
    return b_impl{}
}

我不知道这是否是一种不好的做法,可能有些情况需要重新设计而不是这样做。

【讨论】:

    猜你喜欢
    • 2013-11-06
    • 2011-04-12
    • 2010-10-04
    • 2017-06-10
    • 2015-10-04
    • 2023-03-23
    • 2014-11-20
    • 1970-01-01
    相关资源
    最近更新 更多