Go - 处理多种类型的相同接口
Go - Same interface to handle multiple types
我正在处理允许创建 Device
记录的多个供应商 API,但正如预期的那样,它们代表不同的设备。基础示例(关注厂商ID类型差异)-
Vendor1#Device 使用整数 ID { ID: <int>, ...vendor1 specific details }
Vendor2#设备使用 UUID { UUID: <string>, ...vendor2 specific details }
由于供应商之间的结构不同,我计划将这些(设备记录)保存在一个 MongoDB 集合中,因此我创建了以下接口以供应用程序代码使用 -
type Device struct {
Checksum string
RemoteID ?? # vendor1 uses int and vendor2 uses uuid
}
type DataAccessor interface {
FindDeviceByChecksum(string) (Device, error)
InsertDevice(Device) (bool, error)
}
这将从 orchestration/service 对象中使用,例如 -
type Adapter interface {
AssignGroupToDevice(GroupID, DeviceRemoteID ??) (bool, error)
}
type Orchestrator struct {
da DataAccessor
vendorAPI Adapter
}
// Inside orchestrator#Assign method
device, _ := o.da.FindDeviceByChecksum("checksum from arg")
.
.
o.vendorAPI.AssignGroupToDevice("groupID from arg", device.RemoteID ??)
// The above method calls out vendor's HTTP API and passes the json payload built from args
//
如您所见,我无法输入 RemoteID
或 DeviceRemoteID
的类型。我有什么选择来务实地处理这个问题?一个空接口会让我在接口实现中编写类型开关吗?泛型?或者是其他东西?我很困惑。
您的应用程序代码根本不应该关心实际供应商及其 API。
尝试在您的应用程序中定义您将在您的域中使用的一些核心实体包。这可以是您决定的任何内容,不应依赖于外部依赖项。
该服务将定义执行适当的 BL(查找、插入、分配组 ID)所需的接口
例如:
实体包可以device
package device
type Device struct {
Checksum string
RemoteID string
}
请注意,您可以将 RemoteID
定义为字符串。对于每个供应商,您将拥有一个适配器,它了解应用程序实体和外部供应商 API。每个适配器都需要实现服务所需的接口。
type DeviceRepository interface {
FindDeviceByChecksum(string) (device.Device, error)
InsertDevice(device.Device) (bool, error)
}
type VendorAdapter interface {
AssignGroupToDevice(GroupID, DeviceRemoteID string) (bool, error)
}
type Orchestrator struct {
deviceRepo DeviceRepository
vendorAdapter VendorAdapter
}
// Inside orchestrator#Assign method
device, err := o.deviceRepo.FindDeviceByChecksum("checksum from arg")
if err != nil {...}
.
.
o.vendorAdapter.AssignGroupToDevice("groupID from arg", device.RemoteID)
//
这里可以注意几点:
- 定义了服务包中的接口。 (在你所在的地方定义接口using/requiring他们)
DeviceRepository
:这是数据层,负责持久化你的实体(进入mongo)(repo只是我习惯的约定,不一定要调用回购:)
VendorAdapter
:适配器到实际供应商。该服务不知道此供应商适配器的实现。它不关心它对 remote-id 做了什么。使用 int 的供应商 API 只会将字符串转换为 int。
Of course naming is optional. You can use DeviceProvider instead of VendoreAdapter for example, anything that will make sense to you and your team.
这是适配器的全部要点,它们正在将 from/to 实体转换为外部实体。从应用程序语言到特定的外部语言,反之亦然。在某种程度上,存储库只是数据库的适配器。
编辑:例如,带有 int remote-id 的供应商适配器只会将字符串转换为 int(除非我完全遗漏了这一点大声笑:):
package vendor2
type adapter struct {
...
}
func (a *adapter) AssignGroupToDevice(groupID, deviceRemoteID string) error {
vendor2RemoteID, err := strconv.Atoi(deviceRemoteID)
if err != nil {...}
// send to vendor api2, using the vendor2RemoteID which is int
}
编辑2:
如果这些供应商之间还有另一个字段不同并且不是原始字段,您仍然可以使用字符串。供应商的适配器只需要将字符串编组为特定供应商的自定义负载。
另一种方式当然是照@blackgreen 说的做,保存为interface{}
您需要做出一些决定:
- 如何将其序列化到数据库
- 如何在供应商的适配器中序列化它
- 你在应用程序中使用它吗?意思是应用程序知道或不知道该值。 (如果是这样,您可能不想将其保存为字符串。也许 :)
回购 - 将其作为 JSON 保存到数据库
供应商适配器 - 会将此 interface{}
转换为 API 需要
的实际负载
但是还有许多其他选项可以处理动态类型,很抱歉我没有完全回答您的问题。解决方案的核心取决于应用程序是否使用它,或者它只是供应商适配器使用的数据。
我正在处理允许创建 Device
记录的多个供应商 API,但正如预期的那样,它们代表不同的设备。基础示例(关注厂商ID类型差异)-
Vendor1#Device 使用整数 ID { ID: <int>, ...vendor1 specific details }
Vendor2#设备使用 UUID { UUID: <string>, ...vendor2 specific details }
由于供应商之间的结构不同,我计划将这些(设备记录)保存在一个 MongoDB 集合中,因此我创建了以下接口以供应用程序代码使用 -
type Device struct {
Checksum string
RemoteID ?? # vendor1 uses int and vendor2 uses uuid
}
type DataAccessor interface {
FindDeviceByChecksum(string) (Device, error)
InsertDevice(Device) (bool, error)
}
这将从 orchestration/service 对象中使用,例如 -
type Adapter interface {
AssignGroupToDevice(GroupID, DeviceRemoteID ??) (bool, error)
}
type Orchestrator struct {
da DataAccessor
vendorAPI Adapter
}
// Inside orchestrator#Assign method
device, _ := o.da.FindDeviceByChecksum("checksum from arg")
.
.
o.vendorAPI.AssignGroupToDevice("groupID from arg", device.RemoteID ??)
// The above method calls out vendor's HTTP API and passes the json payload built from args
//
如您所见,我无法输入 RemoteID
或 DeviceRemoteID
的类型。我有什么选择来务实地处理这个问题?一个空接口会让我在接口实现中编写类型开关吗?泛型?或者是其他东西?我很困惑。
您的应用程序代码根本不应该关心实际供应商及其 API。
尝试在您的应用程序中定义您将在您的域中使用的一些核心实体包。这可以是您决定的任何内容,不应依赖于外部依赖项。
该服务将定义执行适当的 BL(查找、插入、分配组 ID)所需的接口
例如:
实体包可以device
package device
type Device struct {
Checksum string
RemoteID string
}
请注意,您可以将 RemoteID
定义为字符串。对于每个供应商,您将拥有一个适配器,它了解应用程序实体和外部供应商 API。每个适配器都需要实现服务所需的接口。
type DeviceRepository interface {
FindDeviceByChecksum(string) (device.Device, error)
InsertDevice(device.Device) (bool, error)
}
type VendorAdapter interface {
AssignGroupToDevice(GroupID, DeviceRemoteID string) (bool, error)
}
type Orchestrator struct {
deviceRepo DeviceRepository
vendorAdapter VendorAdapter
}
// Inside orchestrator#Assign method
device, err := o.deviceRepo.FindDeviceByChecksum("checksum from arg")
if err != nil {...}
.
.
o.vendorAdapter.AssignGroupToDevice("groupID from arg", device.RemoteID)
//
这里可以注意几点:
- 定义了服务包中的接口。 (在你所在的地方定义接口using/requiring他们)
DeviceRepository
:这是数据层,负责持久化你的实体(进入mongo)(repo只是我习惯的约定,不一定要调用回购:)VendorAdapter
:适配器到实际供应商。该服务不知道此供应商适配器的实现。它不关心它对 remote-id 做了什么。使用 int 的供应商 API 只会将字符串转换为 int。
Of course naming is optional. You can use DeviceProvider instead of VendoreAdapter for example, anything that will make sense to you and your team.
这是适配器的全部要点,它们正在将 from/to 实体转换为外部实体。从应用程序语言到特定的外部语言,反之亦然。在某种程度上,存储库只是数据库的适配器。
编辑:例如,带有 int remote-id 的供应商适配器只会将字符串转换为 int(除非我完全遗漏了这一点大声笑:):
package vendor2
type adapter struct {
...
}
func (a *adapter) AssignGroupToDevice(groupID, deviceRemoteID string) error {
vendor2RemoteID, err := strconv.Atoi(deviceRemoteID)
if err != nil {...}
// send to vendor api2, using the vendor2RemoteID which is int
}
编辑2: 如果这些供应商之间还有另一个字段不同并且不是原始字段,您仍然可以使用字符串。供应商的适配器只需要将字符串编组为特定供应商的自定义负载。
另一种方式当然是照@blackgreen 说的做,保存为interface{}
您需要做出一些决定:
- 如何将其序列化到数据库
- 如何在供应商的适配器中序列化它
- 你在应用程序中使用它吗?意思是应用程序知道或不知道该值。 (如果是这样,您可能不想将其保存为字符串。也许 :)
回购 - 将其作为 JSON 保存到数据库
供应商适配器 - 会将此 interface{}
转换为 API 需要
但是还有许多其他选项可以处理动态类型,很抱歉我没有完全回答您的问题。解决方案的核心取决于应用程序是否使用它,或者它只是供应商适配器使用的数据。