Golang中接口方法返回另一个接口的实现问题

Golang中接口方法返回另一个接口的实现问题 关于在Go中正确实现接口的问题,特别是涉及使用链式方法的第三方包时。我整理了一个示例项目,以便您能理解这个问题。

package main

import (
	myAPI "github.com/hashicorp/vault/api"
)

var myClient *myAPI.Client

type MyProvider interface {
	GetClient() MyAPIClient
}

type MyAPIClient interface {
	// 我想这样做,但它不工作
	Logical() MyAPILogical
	// 不过这样是可行的
	// Logical() *myAPI.Logical
}

type MyAPILogical interface {
	Write(path string, data map[string]interface{}) (*myAPI.Secret, error)
}

type Provider struct {}

func PublicFunctionIWantToTest(provider MyProvider) {
	client := provider.GetClient()
	// 我们通常在这里对 `client` 变量做一些操作,但重要的是我们稍后会传递它
	privateFunctionThatIsUsedInTheTest(client)
}

func privateFunctionThatIsUsedInTheTest(client MyAPIClient) (*myAPI.Secret, error) {
	return client.Logical().Write(
		"/here/goes/some/path",
		map[string]interface{}{
			"key": "value",
		},
	)
}

func NewProvider() MyProvider {
	return Provider{}
}

func (p Provider) GetClient() MyAPIClient {
	return myClient
}

// 空函数,仅为了使此示例能够构建
func main() {}

如您所见,该包有一个链式方法 Logical().Write()。由于我想为 PublicFunctionIWantToTest 创建测试,我希望将所有功能通过接口传递,以便可以使用 mockery 为其创建模拟。

不幸的是,我在 MyAPIClientMyAPILogical 接口上遇到了问题。从包的文档(api package - github.com/hashicorp/vault/api - Go Packages)中可以看到,Logical() 方法返回一个 Logical 实例,我希望让接口方法返回另一个接口 MyAPILogical(第15行)。但这并不奏效,GetClient() 方法(第47行)出现错误,提示我没有正确实现接口。我该如何解决?

cannot use myClient (variable of type *api.Client) as MyAPIClient value in return statement: *api.Client does not implement MyAPIClient (wrong type for method Logical)
		have Logical() *api.Logical
		want Logical() 

非常感谢。


更多关于Golang中接口方法返回另一个接口的实现问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

感谢你的帮助 @skillian

更多关于Golang中接口方法返回另一个接口的实现问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我不熟悉那个包,也还没完全理解你试图实现的大局,但你之所以遇到那个错误,正如你所说,是因为 (*api.Client).Logical 返回的是 *api.Logical,而不是 MyAPILogical。即使 *api.Logical 实现了该接口,MyAPIClient 接口也必须被精确匹配。

一个变通方法是将 *api.Client 类型包装到一个结构体中,并通过它来实现你的 Logical 函数:

type myAPIClient struct{ *myAPI.Client }

func (c myAPIClient) Logical() MyAPILogical {
	return c.Client.Logical()
}

var myClient myAPIClient

在Go中,接口实现是隐式的,但必须满足方法签名完全匹配。您遇到的问题是因为*api.ClientLogical()方法返回的是*api.Logical,而您的接口要求返回MyAPILogical。虽然*api.Logical实现了MyAPILogical接口(因为它有Write方法),但返回类型不匹配。

解决方案是使用适配器模式包装原始类型。以下是修正后的代码:

package main

import (
	myAPI "github.com/hashicorp/vault/api"
)

var myClient *myAPI.Client

type MyProvider interface {
	GetClient() MyAPIClient
}

type MyAPIClient interface {
	Logical() MyAPILogical
}

type MyAPILogical interface {
	Write(path string, data map[string]interface{}) (*myAPI.Secret, error)
}

// 适配器包装*api.Client
type clientAdapter struct {
	client *myAPI.Client
}

func (c *clientAdapter) Logical() MyAPILogical {
	return &logicalAdapter{logical: c.client.Logical()}
}

// 适配器包装*api.Logical
type logicalAdapter struct {
	logical *myAPI.Logical
}

func (l *logicalAdapter) Write(path string, data map[string]interface{}) (*myAPI.Secret, error) {
	return l.logical.Write(path, data)
}

type Provider struct{}

func PublicFunctionIWantToTest(provider MyProvider) {
	client := provider.GetClient()
	privateFunctionThatIsUsedInTheTest(client)
}

func privateFunctionThatIsUsedInTheTest(client MyAPIClient) (*myAPI.Secret, error) {
	return client.Logical().Write(
		"/here/goes/some/path",
		map[string]interface{}{
			"key": "value",
		},
	)
}

func NewProvider() MyProvider {
	return Provider{}
}

func (p Provider) GetClient() MyAPIClient {
	return &clientAdapter{client: myClient}
}

func main() {}

这个方案创建了两个适配器:

  1. clientAdapter包装*api.Client,实现MyAPIClient接口
  2. logicalAdapter包装*api.Logical,实现MyAPILogical接口

这样您就可以使用mockery为MyAPIClientMyAPILogical生成模拟对象进行测试。

回到顶部