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 ?

王林
王林avant
2024-02-10 14:06:071153parcourir

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

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

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

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

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{} },
}

https://www.php.cn/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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer