卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章35181本站已运行394

如何避免需要动态选择类型的代码重复?

如何避免需要动态选择类型的代码重复?

在编写代码时,我们经常会遇到需要根据不同条件选择不同类型的代码的情况。这种情况下,如果没有合适的处理方式,代码可能会变得冗长而重复。那么,如何避免这种代码重复呢?php小编百草为大家带来了一些简单而有效的解决方案,让我们一起来看看吧!

问题内容

以下代码是视频流解析器的简化示例。输入是包含视频和音频帧的二进制数据。每个框架由以下部分组成:

  1. 帧类型标志,指示是视频帧还是音频帧
  2. 标题
  3. 有效负载

目标是解析流,从标头和有效负载中提取字段。

所以,第一种方法是:

package main
import (
    "fmt"
    "encoding/binary"
    "bytes"
)

type Type byte

const (
    Video  Type = 0xFC
    Audio   Type = 0xFA
)

var HMap = map[Type]string {
    Video:   "Video",
    Audio:   "Audio",
}

type CommonHeader struct {
    Type      Type
}

type HeaderVideo struct {
    Width       uint16
    Height      uint16
    Length      uint32
}

type HeaderAudio struct {
    SampleRate  uint16
    Length      uint16
}


func main() {
    data := bytes.NewReader([]byte{0xFC, 0x80, 0x07, 0x38, 0x04, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xAF, 0xFA, 0x10, 0x00, 0x01, 0x00, 0xFF})
    var cHeader CommonHeader
    var dataLength int
    for {
        err := binary.Read(data, binary.LittleEndian, &cHeader)
        if err != nil {
            break
        }
        fmt.Println(HMap[cHeader.Type])
        switch cHeader.Type {
            case Video:
                var info HeaderVideo
                binary.Read(data, binary.LittleEndian, &info)
                dataLength = int(info.Length)
                fmt.Println(info)
            case Audio:
                var info HeaderAudio
                binary.Read(data, binary.LittleEndian, &info)
                dataLength = int(info.Length)
                fmt.Println(info)
        }
        payload := make([]byte, dataLength)
        data.Read(payload)
        fmt.Println(payload)
    }
}

它有效,但我不喜欢 switch 案例中的代码重复。本质上,我们必须重复相同的代码,只是因为帧类型不同。

尝试避免重复的一种方法是:

package main
import (
    "fmt"
    "encoding/binary"
    "bytes"
)

type Type byte

const (
    Video  Type = 0xFC
    Audio   Type = 0xFA
)

var HMap = map[Type]string {
    Video:   "Video",
    Audio:   "Audio",
}

type CommonHeader struct {
    Type      Type
}

type Header interface {
    GetLength() int
}

type HeaderVideo struct {
    Width       uint16
    Height      uint16
    Length      uint32
}

func (h HeaderVideo) GetLength() int {
    return int(h.Length)
}

type HeaderAudio struct {
    SampleRate  uint16
    Length      uint16
}

func (h HeaderAudio) GetLength() int {
    return int(h.Length)
}

var TMap = map[Type]func() Header {
    Video:     func() Header { return &HeaderVideo{} },
    Audio:     func() Header { return &HeaderAudio{} },
}

func main() {
    data := bytes.NewReader([]byte{0xFC, 0x80, 0x07, 0x38, 0x04, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xAF, 0xFA, 0x10, 0x00, 0x01, 0x00, 0xFF})
    var cHeader CommonHeader
    for {
        err := binary.Read(data, binary.LittleEndian, &cHeader)
        if err != nil {
            break
        }
        fmt.Println(HMap[cHeader.Type])
        info := TMap[cHeader.Type]()
        binary.Read(data, binary.LittleEndian, info)
        fmt.Println(info)
        payload := make([]byte, info.GetLength())
        data.Read(payload)
        fmt.Println(payload)
    }
}

也就是说,我们通过引入 TMap 映射来实现动态类型选择,该映射允许根据帧类型创建正确结构的实例。但是,此解决方案的代价是对每种帧类型重复 GetLength() 方法。

我觉得很令人不安的是,似乎没有办法完全避免重复。 我是否遗漏了什么,或者只是语言的限制?

这是一个相关的问题(实际上是由同一个问题触发的),但是,它的前提忽略了动态类型选择的需要,因此公认的解决方案(使用泛型)没有帮助。

解决方法

King 的答案要求对用于编码长度的每个整数类型进行重复。 Mondarin 的答案 使用可怕的 reflect 包。这是避免这两个问题的解决方案。这个答案是基于国王的答案。

使用 GetLength() 方法声明泛型类型。

type Length[T uint8 | uint16 | uint32 | uint64] struct { Length T }

func (l Length[T]) GetLength() int { return int(l.Length) }

从每个标头类型中删除 GetLength 方法。在每个标头类型中嵌入通用长度类型:

type HeaderVideo struct {
    Width  uint16
    Height uint16
    Length[uint32]
}

type HeaderAudio struct {
    SampleRate uint16
    Length[uint16]
}

在问题中声明 TMap as。 GetLength 方法由嵌入字段提供。

var TMap = map[Type]func() Header{
    Video: func() Header { return &HeaderVideo{} },
    Audio: func() Header { return &HeaderAudio{} },
}

https://www.php.cn/link/ceb9f6b8ffa77c49b6b4570ea19c76bf

(与问题中的代码一样,此答案通过 binary.Read 函数间接使用 reflect 包。reflect 包是保持代码干燥的好工具。)

卓越飞翔博客
上一篇: 在 C++ 中嵌入 python 时出现 AttributeError (Ubuntu)
下一篇: 返回列表
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏