Go中的接口管理

我知道以前曾多次以各种形式询问过这个问题,但我似乎无法以我需要的方式实施我正在学习的内容。任何帮助表示赞赏。


我有一系列交易所,它们都实现了大致相同的 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.go


var exchange *Api

现在我的想法是我应该能够调用类似的东西exchange.GetBalance()并且它会使用GetBalance上面的函数。我还需要某种铸造?我在这里很迷路。可以exchange是 Binance 或 Kraken——这在运行时决定。其他一些代码基本上调用一个GetExchange函数,该函数返回所需 API 对象的实例(已经在 BinanceApi/KrakenApi 中转换)


我知道继承和多态性不像其他语言那样工作,因此我非常困惑。我很难知道这里需要去哪里。Go 似乎需要大量烦人的代码来完成其他语言的动态工作😓


慕运维8079593
浏览 107回答 1
1回答

冉冉说

使用*exchanges.Api很奇怪。您想要实现给定接口的东西。底层类型是什么(无论是指针还是值接收器)并不重要,因此请改用exchanges.Api。但是,还有另一个问题。在 golang 中,接口是隐式的(有时称为鸭式接口)。一般来说,这意味着接口不是在实现它的包中声明的,而是在依赖给定接口的包中声明的。有人说你应该在返回什么值方面是自由的,但在你接受的参数方面应该是限制性的。在您的情况下,这归结为您拥有类似包裹的东西api,看起来有点像这样:package apifunc NewKraken(args ...any) *KrakenExchange {   // ...}func NewBinance(args ...any) *BinanceExchange {}然后在你的其他包中,你会有这样的东西:package kraken // or maybe this could be an exchange packagetype 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 Dependencytype 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)长话短说它的要点是:接口就是接口,不要使用指向接口的指针在依赖于它们(即用户)的包中声明接口,而不是实现(提供者)端。只有在给定的包中真正使用它们时,才在接口中声明方法。使用中央的、总体的界面使这更难做到。与用户一起声明依赖接口使得自文档代码成为可能单元测试和模拟/存根更容易做,并且以这种方式自动化
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go