【问题标题】:Golang proper use of interfacesGolang 正确使用接口
【发布时间】:2018-12-25 11:28:52
【问题描述】:

我是 Go 新手,遇到了一个我不确定如何解决的情况。我正在编写一些代码,它以原始字节的形式获取 DNS 数据包并返回一个名为 DNSPacket 的结构。

结构如下所示

type DNSPacket struct {
    ...some fields
    Questions  []Question
    Answers    []Answer
    ...some more fields
}

我遇到的问题是 Answers 类型,看起来像这样。

 type Answer struct {
    Name     string
    Type     int
    Class    int
    TTL      uint32
    RdLength int
    Data     []byte
}

根据答案的类型,Data 字段的解码方式必须不同。例如,如果答案是 A 记录(类型 1),则数据只是一个 ipv4 地址。但是,如果答案是 SRV 记录(类型 33),则数据包含在字节切片中编码的 portpriorityweighttarget

我认为如果我可以在 Answer 上有一个名为 DecodeData() 的方法,它会根据类型返回正确的数据,但由于 Go 中没有覆盖或继承,我不确定如何解决这个问题。我尝试使用一个接口来解决这个问题,但它不会编译。我尝试了类似的东西

type DNSRecordType interface {
    Decode(data []byte)
}


type RecordTypeSRV struct {
   target string
   ...more fields
}
//to 'implement' the DNSRecordType interface
func (record *RecordTypeSRV) Decode(data []byte) {
    //do the work to decode appropriately and set
    //the fields on the record
}

然后在Answer方法中

func (a *Answer) DecodeData() DNSRecordType {
    if a.Type === SRVType {
       record := RecordTypeSRV{}
       record.Decode(a.Data)
       return record
    }

    //do something similar for other record types
 }

拥有单一答案类型但能够根据其类型返回不同类型的答案数据的正确 Go 方式是什么? 抱歉,如果这是一个完全初学者的问题,因为我对 Go 还是很陌生。

谢谢!

【问题讨论】:

  • 一般这种事情是用Discriminated Union来完成的,但是我不知道Go中有没有这样的事情。

标签: go dns


【解决方案1】:

让我总结一下你的问题。

您有一个带有答案列表的 DNS 数据包。根据答案类型,您必须处理答案中的数据。

type DNSPacket struct {
    ...some fields
    Questions  []Question
    Answers    []Answer
    ...some more fields
}
type Answer struct {
    Name     string
    Type     int
    Class    int
    TTL      uint32
    RdLength int
    Data     []byte
}

回答 让我们创建一个应该被实现来处理数据的接口。

type PacketProcessor interface {
    Process(Answer)
}

让SRV实现PacketProcessor

type SRV struct {
    ...
}

func (s *SRV) Process(a Answer) {
    ...
}

你的处理逻辑应该如下

func (a *Answer) Process() {
    var p PacketProcessor
    switch a.Type {
        case SRVType:
        p = &SRV{}
        ...
        //other cases
    }

    //finally
    p.Process(*a)
}

希望它有所帮助:)。 有一个基于 Gurgaon 的 golang 社区,随时准备帮助开发人员解决他们的问题。 您可以通过slack加入社区

【讨论】:

  • 您在总结我的问题时一针见血。我扩展了 Process 方法,所以签名现在看起来像这样 func (a *Answer) Process() PacketProcessor {...} 。这样,Answer 的消费者就可以使用结果值。我有这样的工作,但正如你所见,我需要进行类型断言。 record := decoded.Answers[0].Process() t, ok := record.(*dnsPacket.RecordTypeA)
【解决方案2】:

据我所知,要返回不同的类型,返回参数必须是接口。所以你可以像这样简单地声明函数:

func (a *Answer) DecodeData() (mode modeType, value interface{}) {}

mode表示值为A记录或SRV记录,你可以通过value字段返回任何你想要的。

函数调用者可以根据模式

处理

如果您希望代码更优雅,您可以为每种模式定义不同的值结构。那么调用者可能会如下行为:

type modeType int

const (
    ARecord modeType = 1
    SRVRecord modeType = 2
)

switch mode {
    case ARecord:
    // do something
    case SRVRecord:
    // do something
} 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-01
    • 2018-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2017-05-17
    • 2016-10-15
    相关资源
    最近更新 更多