Maison > développement back-end > Golang > Comment éviter la duplication de code qui nécessite une sélection dynamique de types ?

Comment éviter la duplication de code qui nécessite une sélection dynamique de types ?

王林
Libérer: 2024-02-10 14:06:07
avant
1142 Les gens l'ont consulté

Comment éviter la duplication de code qui nécessite une sélection dynamique de types ?

Lors de l'écriture de code, nous rencontrons souvent des situations où nous devons choisir différents types de code en fonction de différentes conditions. Dans ce cas, sans une manipulation appropriée, le code peut devenir verbeux et répétitif. Alors, comment éviter cette duplication de code ? L'éditeur PHP Baicao vous a apporté des solutions simples et efficaces, jetons-y un œil !

Contenu de la question

Le code suivant est un exemple simplifié d'analyseur de flux vidéo. L'entrée est constituée de données binaires contenant des images vidéo et audio. Chaque cadre se compose des parties suivantes :

  1. Drapeau de type d'image, indiquant s'il s'agit d'une image vidéo ou d'une image audio
  2. Titre
  3. Charge utile

L'objectif est d'analyser le flux, d'extraire les champs des en-têtes et de la charge utile.

Donc, la première méthode est :

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)
    }
}
Copier après la connexion

Cela fonctionne, mais je n'aime pas la duplication de code dans le cas switch. Essentiellement, nous devons répéter le même code, simplement parce que le type de trame est différent.

Une façon d’essayer d’éviter la duplication est :

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)
    }
}
Copier après la connexion

C'est-à-dire que nous introduisons la méthode TMap 映射来实现动态类型选择,该映射允许根据帧类型创建正确结构的实例。但是,此解决方案的代价是对每种帧类型重复 GetLength().

Je trouve très troublant qu'il semble n'y avoir aucun moyen d'éviter complètement la duplication. Est-ce qu'il me manque quelque chose, ou est-ce juste une limitation de la langue ?

Il s'agit d'une question connexe (en fait déclenchée par le même problème), cependant, sa prémisse ignore la nécessité d'une sélection de type dynamique, donc la solution acceptée (utilisant des génériques) n'aide pas.

Solution de contournement

La réponse de King nécessite qu'elle soit répétée pour chaque type entier utilisé pour coder la longueur. La réponse de Mondarin utilise le package génial reflect. Voici une solution pour éviter les deux problèmes. Cette réponse est basée sur la réponse de King.

Déclarez les types génériques à l'aide de la méthode GetLength().

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

func (l Length[T]) GetLength() int { return int(l.Length) }
Copier après la connexion

Supprimez la méthode GetLength de chaque type d'en-tête. Intégrez un type de longueur commun dans chaque type d'en-tête : 

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

type HeaderAudio struct {
    SampleRate uint16
    Length[uint16]
}
Copier après la connexion

Indiqué dans la question TMap as。 GetLengthLa méthode est fournie par un champ intégré.

var TMap = map[Type]func() Header{
    Video: func() Header { return &HeaderVideo{} },
    Audio: func() Header { return &HeaderAudio{} },
}
Copier après la connexion

//m.sbmmt.com/link/ceb9f6b8ffa77c49b6b4570ea19c76bf

(Comme le code dans la question, cette réponse utilise le package binary.Read 函数间接使用 reflect 包。reflect indirectement via la fonction binary.Read.

Les packages sont un excellent outil pour garder votre code au sec.) 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal