在Go中获得不同但相似类型的数组行为的最佳方法?

时间:2016-12-30 21:47:17

标签: go data-structures

方案: 我有几个不同的客户端,每个客户端与不同的API交互。

这些客户的数据字段相同:

type clientX struct {
   key string
   secret string
   client *http.Client
}

然而,这些客户各有许多方法(彼此不同):

func (c *ClientX) someMethod() (*ResponseType, error) {
    //code
}

随着时间的推移,客户端数量可能会发生变化,因为会添加对新API的支持,或者某些API会脱机。因此,主程序包中的所有函数都需要是模块化的,并且适合于接受可变数量的客户端作为参数。

解决这个问题的最佳方法是什么? 我不能将客户端放在一个数组中,因为它们是不同的类型。

我正在考虑的想法:

  1. 首先想到的解决方案是一个类型为interface {}的数组。但是我关心的是[]接口{}的性能以及我在迭代数组时识别客户端类型所带来的代码膨胀(类型断言)。

  2. 我不像我想的那样接受继承方面的教育,所以我不确定这是否有效。我正在设想创建一个包含密钥,密钥和客户端数据字段的父类Client。特定客户端将是继承自父类Client的子类,然后定义特定于该客户端的所有方法。根据我的基本理解,我可以通过将数组类型定义为Client来将所有这些客户端放入数组中。这让我对数组中元素的行为方式有点困惑,因为它不是ClientX类型,而是更通用的Client类型。这是否会导致不得不再次输入断言,即解决方案1中的问题?如果我不得不使用ID并断言类型,那么在类型接口数组上使用Client类型的数组会有什么性能优势吗?

  3. 让客户端(clientA,clientB,clientC)成为全局变量。任何函数都可以访问它们,所以我不必将它们作为参数传递。为了处理变量客户端(数字是在运行时决定的),我会有一个clientsEnabled map [string] bool,用于识别要使用哪些客户端以及忽略哪些客户端。这似乎是最小的代码膨胀。但是,我对使用全局变量持谨慎态度,除非绝对必要,否则我总是被教导避免。

  4. SO社区的另一个解决方案

  5. 很想得到一些反馈,谢谢。

2 个答案:

答案 0 :(得分:1)

根据给出的信息,您应该定义一个封装各种客户端类型共有的接口类型。这将是实际实现你在#2中谈论的内容的方法。请记住,Go没有类和子类的概念,所以如果你有一个类型ClientX,然后将其嵌入到其他类型中(最接近的东西必须进行子类化),那么其他类型不能在类型中使用预计会ClientX。拥有一个可以代表几种不同底层类型的类型的唯一方法是定义一个接口。

除了ClientX结构中的公共字段之外,我假设您必须具有某种特定于客户端的处理函数。然后,该功能将执行任何特定于客户端的操作。 (如果不了解有关您正在做的事情的详细信息,很难更具体。)

这是一个简单的例子:

package main

import (
    "fmt"
    "net/http"
)

type client interface {
    getKey() string
    getSecret() string
    getHttpClient() *http.Client
    handler()
}

type clientX struct {
    key    string
    secret string
    client *http.Client
}

func (c *clientX) getKey() string {
    return c.key
}

func (c *clientX) getSecret() string {
    return c.secret
}

func (c *clientX) getHttpClient() *http.Client {
    return c.client
}

type clientOne struct {
    clientX
    data1 string
    data2 int
    // ...
}

func (c *clientOne) handler() {
    fmt.Printf("clientOne handler: data1: %q\n", c.data1)
}

func main() {
    c1 := &clientOne{
        clientX: clientX{
            key:    "abc",
            secret: "def",
        },
        data1: "some data",
    }

    var clients []client

    clients = append(clients, c1)

    for _, c := range clients {
        fmt.Printf("key %q secret %q\n", c.getKey(), c.getSecret())
        c.handler()
    }
}

答案 1 :(得分:1)

首先,golang中没有继承。另外,我强烈建议您阅读interfaces in golang

至于这个问题,具体而言,我不是存储 interface {} 实例,而是引入命令接口并为应用程序的每个API实现它必须工作。这样的事情:https://play.golang.org/p/t5Kldpbu-P

由于您提到客户端命令之间没有共同的行为,我不会为他们的方法引入接口,除非它们之间存在相互依赖关系。在这种情况下,接口将使它们易于单元测试(创建一个实现接口的模拟)。

这很容易扩展。