Heim >Backend-Entwicklung >Golang >Wie kann eine Codeduplizierung vermieden werden, die eine dynamische Auswahl von Typen erfordert?

Wie kann eine Codeduplizierung vermieden werden, die eine dynamische Auswahl von Typen erfordert?

王林
王林nach vorne
2024-02-10 14:06:071145Durchsuche

Wie kann eine Codeduplizierung vermieden werden, die eine dynamische Auswahl von Typen erfordert?

Beim Schreiben von Code stoßen wir häufig auf Situationen, in denen wir basierend auf unterschiedlichen Bedingungen unterschiedliche Codetypen auswählen müssen. In diesem Fall kann der Code ohne ordnungsgemäße Handhabung ausführlich und sich wiederholend werden. Wie kann man also diese Codeduplizierung vermeiden? Der PHP-Editor Baicao hat Ihnen einige einfache und effektive Lösungen präsentiert, schauen wir uns das an!

Frageninhalt

Der folgende Code ist ein vereinfachtes Beispiel eines Videostream-Parsers. Bei der Eingabe handelt es sich um Binärdaten, die Video- und Audioframes enthalten. Jeder Rahmen besteht aus folgenden Teilen:

  1. Frame-Typ-Flag, das angibt, ob es sich um einen Video-Frame oder einen Audio-Frame handelt
  2. Titel
  3. Nutzlast

Das Ziel besteht darin, den Stream zu analysieren, Felder aus Headern und Nutzdaten zu extrahieren.

Die erste Methode ist also:

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)
    }
}

Es funktioniert, aber mir gefällt die Codeduplizierung im switch-Fall nicht. Im Wesentlichen müssen wir denselben Code wiederholen, nur weil der Rahmentyp unterschiedlich ist.

Eine Möglichkeit, Doppelarbeit zu vermeiden, ist:

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)
    }
}

Das heißt, wir führen die TMap 映射来实现动态类型选择,该映射允许根据帧类型创建正确结构的实例。但是,此解决方案的代价是对每种帧类型重复 GetLength()-Methode ein.

Ich finde es sehr beunruhigend, dass es anscheinend keine Möglichkeit gibt, Doppelarbeit vollständig zu vermeiden. Übersehe ich etwas oder ist es nur eine Einschränkung der Sprache?

Dies ist eine verwandte Frage (die tatsächlich durch dasselbe Problem ausgelöst wird), ihre Prämisse ignoriert jedoch die Notwendigkeit einer dynamischen Typauswahl, sodass die akzeptierte Lösung (Verwendung von Generika) nicht hilft.

Workaround

Kings Antwort erfordert, dass sie für jeden Ganzzahltyp wiederholt wird, der zum Codieren der Länge verwendet wird. Mondarins Antwort verwendet das tolle reflect-Paket. Hier ist eine Lösung, um beide Probleme zu vermeiden. Diese Antwort basiert auf Kings Antwort.

Deklarieren Sie generische Typen mit der Methode GetLength().

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

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

Entfernen Sie die GetLength-Methode aus jedem Header-Typ. Betten Sie in jeden Header-Typ einen gemeinsamen Längentyp ein:

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

type HeaderAudio struct {
    SampleRate uint16
    Length[uint16]
}

In der Frage wurde angegeben, dass die TMap as。 GetLength-Methode durch ein eingebettetes Feld bereitgestellt wird.

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

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

(Wie der Code in der Frage verwendet diese Antwort das binary.Read 函数间接使用 reflect 包。reflect-Paket indirekt über die Funktion binary.Read.

Pakete sind ein großartiges Werkzeug, um Ihren Code trocken zu halten.) 🎜

Das obige ist der detaillierte Inhalt vonWie kann eine Codeduplizierung vermieden werden, die eine dynamische Auswahl von Typen erfordert?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen