When writing code, we often encounter situations where we need to choose different types of code based on different conditions. In this case, without proper handling, the code may become verbose and repetitive. So, how to avoid this code duplication? PHP editor Baicao has brought you some simple and effective solutions, let us take a look!
The following code is a simplified example of a video stream parser. The input is binary data containing video and audio frames. Each frame consists of the following parts:
The goal is to parse the stream, extracting fields from headers and payload.
So, the first method is:
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) } }
It works, but I don't like the code duplication in the switch
case. Essentially, we have to repeat the same code, just because the frame type is different.
One way to try and avoid duplication is:
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) } }
That is, we implement dynamic type selection by introducing a TMap
mapping that allows the creation of instances of the correct structure based on the frame type. However, this solution comes at the cost of repeating the GetLength()
method for each frame type.
What I find very disturbing is that there seems to be no way to completely avoid duplication. Am I missing something, or is it just a limitation of the language?
This is a related question (actually triggered by the same problem), however, its premise ignores the need for dynamic type selection, so the accepted solution (using generics) doesn't help.
King's answer requires that it be repeated for each integer type used to encode the length. Mondarin's answer Use the terrible reflect
package. Here is the solution to avoid both problems. This answer is based on King's answer.
Use the GetLength() method to declare a generic type.
type Length[T uint8 | uint16 | uint32 | uint64] struct { Length T } func (l Length[T]) GetLength() int { return int(l.Length) }
Remove GetLength method from each header type. Embed a common length type in each header type:
type HeaderVideo struct { Width uint16 Height uint16 Length[uint32] } type HeaderAudio struct { SampleRate uint16 Length[uint16] }
State TMap
as in the question. The GetLength
method is provided by the embedded field.
var TMap = map[Type]func() Header{ Video: func() Header { return &HeaderVideo{} }, Audio: func() Header { return &HeaderAudio{} }, }
//m.sbmmt.com/link/ceb9f6b8ffa77c49b6b4570ea19c76bf
(Like the code in the question, this answer uses the reflect
package indirectly through the binary.Read
function. The reflect
package is a great tool for keeping your code dry .)
The above is the detailed content of How to avoid code duplication that requires dynamic selection of types?. For more information, please follow other related articles on the PHP Chinese website!