首頁  >  文章  >  後端開發  >  如何避免需要動態選擇類型的程式碼重複?

如何避免需要動態選擇類型的程式碼重複?

王林
王林轉載
2024-02-10 14:06:071109瀏覽

如何避免需要動態選擇類型的程式碼重複?

在編寫程式碼時,我們經常會遇到需要根據不同條件選擇不同類型的程式碼的情況。在這種情況下,如果沒有合適的處理方式,程式碼可能會變得冗長而重複。那麼,如何避免這種程式碼重複呢? 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 套件是保持程式碼乾燥的好工具。)

以上是如何避免需要動態選擇類型的程式碼重複?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除