为了账号安全,请及时绑定邮箱和手机立即绑定

Go中的接口管理

Go中的接口管理

Go
慕运维8079593 2023-03-15 14:51:17
我知道以前曾多次以各种形式询问过这个问题,但我似乎无法以我需要的方式实施我正在学习的内容。任何帮助表示赞赏。我有一系列交易所,它们都实现了大致相同的 API。例如,它们每个都有一个GetBalance端点。然而,有些有一个或两个独特的东西需要在函数中访问。例如,在调用它的余额 API 时exchange1需要使用 a ,而同时需要变量和变量。这是以后的重要说明。clientexchange2clientclientFutures我的背景是普通的 OOP。显然 Go 在很多方面都是不同的,因此我在这里被绊倒了。我目前的实现和思路如下:在exchanges模块中type Balance struct {    asset       string    available   float64    unavailable float64    total       float64}type Api interface {    GetBalances() []Balance}在Binance模块中type BinanceApi struct {    key           string    secret        string    client        *binance.Client    clientFutures *futures.Client    Api           exchanges.Api}func (v *BinanceApi) GetBalance() []exchanges.Balance {    // Requires v.client and v.clientFutures    return []exchanges.Balance{}}在Kraken模块中type KrakenApi struct {    key           string    secret        string    client        *binance.Client    Api           exchanges.Api}func (v *KrakenApi) GetBalance() []exchanges.Balance {    // Requires v.client    return []exchanges.Balance{}}在main.govar exchange *Api现在我的想法是我应该能够调用类似的东西exchange.GetBalance()并且它会使用GetBalance上面的函数。我还需要某种铸造?我在这里很迷路。可以exchange是 Binance 或 Kraken——这在运行时决定。其他一些代码基本上调用一个GetExchange函数,该函数返回所需 API 对象的实例(已经在 BinanceApi/KrakenApi 中转换)我知道继承和多态性不像其他语言那样工作,因此我非常困惑。我很难知道这里需要去哪里。Go 似乎需要大量烦人的代码来完成其他语言的动态工作😓
查看完整描述

1 回答

?
冉冉说

TA贡献1877条经验 获得超1个赞

使用*exchanges.Api很奇怪。您想要实现给定接口的东西。底层类型是什么(无论是指针还是值接收器)并不重要,因此请改用exchanges.Api。


但是,还有另一个问题。在 golang 中,接口是隐式的(有时称为鸭式接口)。一般来说,这意味着接口不是在实现它的包中声明的,而是在依赖给定接口的包中声明的。有人说你应该在返回什么值方面是自由的,但在你接受的参数方面应该是限制性的。在您的情况下,这归结为您拥有类似包裹的东西api,看起来有点像这样:


package api


func NewKraken(args ...any) *KrakenExchange {

   // ...

}


func NewBinance(args ...any) *BinanceExchange {

}

然后在你的其他包中,你会有这样的东西:


package kraken // or maybe this could be an exchange package


type API interface {

    GetBalances() []types.Balance

}


func NewClient(api API, otherArgs ...T) *KrakenClient {

}

因此,当有人查看此Kraken包的代码时,他们可以立即分辨出需要哪些依赖项以及它适用于哪些类型。额外的好处是,如果 binance 或 kraken 需要额外的未共享的 API 调用,您可以进入并更改特定的依赖项/接口,而不会最终得到一个到处都在使用的庞大的集中式接口,但每次你最终只会使用界面的一个子集。


这种方法的另一个好处是在编写测试时。有像gomockmockgen 这样的工具,它们允许您通过执行以下操作快速生成单元测试的模拟:


package foo


//go:generate go run github.com/golang/mock/mockgen -destination mocks/dep_mock.go -package mocks your/module/path/to/foo Dependency

type Dependency interface {

    // methods here

}

然后运行go generate,它将在your/module/path/to/foo/mocks其中创建一个实现所需接口的模拟对象。在您的单元测试中,导入他的mocks包,您可以执行以下操作:


ctrl := gomock.NewController(t)

dep := mocks.NewDependencyMock(ctrl)

defer ctrl.Finish()

dep.EXPECT().GetBalances().Times(1).Return(data)

k := kraken.NewClient(dep)

bal := k.Balances()

require.EqualValues(t, bal, data)

长话短说

它的要点是:

  • 接口就是接口,不要使用指向接口的指针

  • 在依赖于它们(即用户)的包中声明接口,而不是实现(提供者)端。

  • 只有在给定的包中真正使用它们时,才在接口中声明方法。使用中央的、总体的界面使这更难做到。

  • 与用户一起声明依赖接口使得自文档代码成为可能

  • 单元测试和模拟/存根更容易做,并且以这种方式自动化


查看完整回答
反对 回复 2023-03-15
  • 1 回答
  • 0 关注
  • 106 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信