Define golang struct as type with arbitrary fields?

huangapple go评论75阅读模式
英文:

Define golang struct as type with arbitrary fields?

问题

我对golang和接口的使用都很陌生。我在这里写了一些代码:

type Alerter interface {
	Alert()
}

type AlertConfig struct{}

type Alert struct {
	Config *AlertConfig
}

type AlertConfigurator interface {
	UpdateConfig(key, value interface{}) (*AlertConfig, error)
}

type EmailAlertConfig AlertConfig {
    Recipients []mail.Address,
}

type HTTPAlertConfig AlertConfig {
    Method string
    TargetURL url.URL
}

type HTTPAlert struct {
	Config *HTTPAlertConfig
}

type EmailAlert struct {
	Config *EmailAlertConfig
}

func (ea *EmailAlert) Alert() {
	// this would actually send an email using data in its Config
	return
}

func (ha *HTTPAlert) Alert() {
	// this would actually hit an HTTP endpoint using data in its Config
	return
}

我确定我有各种假设和偏见,这些都妨碍了我表达我想要实现的目标:

我想创建不同类型的“Alert”。我创建的任何警报都应该有一个“Alert()”方法,该方法触发警报实际执行某些操作(例如发送电子邮件或向URL发送POST请求)。

问题在于表示警报的“Config”。不同的警报在其配置中具有不同的字段。然而,对于每种警报类型,_特定_字段 必须存在的。为了实现这一点,我想创建一个基本类型“AlertConfig”,作为一个具有任意字段的结构体,然后定义,比如说,EmailAlertConfig作为类型'AlertConfig',但具有这些特定字段,然后将类型'HTTPAlertConfig'定义为类型'AlertConfig',要求不同的字段。这样,我可以将'Alert'类型定义为具有字段'Config *AlertConfig'。

如果AlertConfig被定义为map[string]interface{},我几乎可以模拟我想要的效果,但在这种情况下,我无法利用golang的检查来验证EmailConfig是否具有所需的字段。

看起来我对此的思考方式是错误的。我需要帮助找到正确的方法,并感谢您的建议。

英文:

I'm very new to golang and the use of interfaces more generally. I've stubbed out some code here:

type Alerter interface {
	Alert()
}

type AlertConfig struct{}

type Alert struct {
	Config *AlertConfig
}

type AlertConfigurator interface {
	UpdateConfig(key, value interface{}) (*AlertConfig, error)
}

type EmailAlertConfig AlertConfig {
    Recipients []mail.Address,
}

type HTTPAlertConfig AlertConfig {
    Method string
    TargetURL url.URL
}

type HTTPAlert struct {
	Config *HTTPAlertConfig
}

type EmailAlert struct {
	Config *EmailAlertConfig
}

func (ea *EmailAlert) Alert() {
	// this would actually send an email using data in its Config
	return
}

func (ha *HTTPAlert) Alert() {
	// this would actually hit an HTTP endpoint using data in its Config
	return
}

I'm sure I have all kinds of assumptions & biases that are hobbling my ability to express what I want to accomplish with this:

I want to create different types of "Alert"s. Any alert I create should have an "Alert()"
method that triggers the Alert to actually do something (send an email, or POST to a URL,
for example.

The trouble comes in representing an Alert's "Config". Different Alerts have different fields
in their Configs. However, for each Alert type, specific fields are required to be there.
To accomplish that I wanted to create a base type "AlertConfig" as a struct with arbitrary fields,
then define, say, EmailAlertConfig as type 'AlertConfig', but having these specific fields, and then
type 'HTTPAlertConfig' as type 'AlertConfig' requiring different fields. This way, I can define
the 'Alert' type as having a field 'Config *AlertConfig'.

I can almost emulate what I want if AlertConfig is defined as map[string]interface{}, but
in this case I can't leverage golang's checking to validate that an EmailConfig has the required fields.

It seems pretty clear that I'm thinking about this the wrong way. I could use a hand in getting on the right track & appreciate your advice.

答案1

得分: 2

声明一个具有常见字段的类型:

 type AlertConfig struct { 
    ExampleCommonField string
 }

将该类型嵌入到实际配置中:

type HTTPAlertConfig struct {
    AlertConfig
    Method string
    TargetURL url.URL
}

根据问题中的代码,可以将警报和配置类型组合起来。

func (ha *HTTPAlertConfig) Alert() {
    // 这将使用接收器中的数据命中一个 HTTP 端点
    return
}
英文:

Declare a type with the common fields:

 type AlertConfig struct { 
    ExampleCommonField string
 }

Embed that type in actual configurations:

type HTTPAlertConfig struct {
    AlertConfig
    Method string
    TargetURL url.URL
}

Based on the code in the question, the alert and config types can be combined.

func (ha *HTTPAlertConfig) Alert() {
    // this will hit an HTTP endpoint using data in the receiver
    return
}

答案2

得分: 2

一种处理这个问题的方法是将配置文件保持纯粹的实现细节:

type HTTPAlert struct {
  Config *HTTPAlertConfig
}

func (a *HTTPAlert) Alert() {...}

type EmailAlert struct {
  Config *EmailAlertConfig
}

func (e *EmailAlert) Alert() {...}

通过这种实现方式,实际的 Alert 实现可以使用特定类型的警报配置,从而解决了初始化配置的问题。

当你创建一个警报实例时,可以执行特定类型的初始化。例如:

var alerts = map[string]func(configFile string) Alert {
   "htmlAlert": NewHTMLAlert,
   "emailAlert": NewEmailAlert,
}

func NewHTMLAlert(configFile string) Alert {
  var alert HTMLAlert
  // 读取文件,初始化警报
  return &alert
}
...
英文:

One way to deal with this problem is to leave configs as purely implementation specific:

type HTTPAlert struct {
  Config *HTTPAlertConfig
}

func (a *HTTPAlert) Alert() {...}

type EmailAlert struct {
  Config *EmailAlertConfig
}

func (e *EmailAlert) Alert() {...}


With this implementation, the actual Alert implementation can use the type-specific alert configuration, leaving the problem of initializing the config.

When you create the instance of an alert, you can perform type-specific initialization. For instance:

var alerts = map[string]func(configFile string) Alert {
   "htmlAlert": NewHTMLAlert,
   "emailAlert" NewEmailAlert,
}

func NewHTMLAlert(configFile string) Alert {
  var alert HTMLAlert
  // Read file, initialize alert
  return &alert
}
...

huangapple
  • 本文由 发表于 2022年1月17日 02:50:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/70733157.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定