Как я могу уменьшить количество повторяющегося кода, связанного с демаршалингом динамических типов c с JSON и отправкой на связанный канал? - PullRequest
0 голосов
/ 04 февраля 2020

Рассмотрим пример, приведенный ниже, он принимает сообщение JSON, которое в конечном итоге разбивается на несколько возможных типов. Как я могу уменьшить или удалить шаблон кода, связанный с добавлением другого типа события.


package main

import (
    "encoding/json"
    "fmt"
)

const input = `
{
    "type": "hello",
    "event": {
        "name": "Picard"
    }
}
`

type EventEnvelope struct {
    Type  string
    Event interface{}
}

type EventHello struct {
    Name string
}

type EventHowdy struct {
    Name string
}

type Display struct {
    formal  chan EventHello
    western chan EventHowdy
}

func newDisplay() *Display {
    return &Display{
        formal:  make(chan EventHello),
        western: make(chan EventHowdy),
    }
}

func (display *Display) run() {
    for {
        select {
        case formal := <-display.formal:
            fmt.Println("Hello", formal.Name)
        case western := <-display.western:
            fmt.Println("Howdy", western.Name)
        }
    }
}

func main() {
    var event json.RawMessage
    env := EventEnvelope{
        Event: &event,
    }
    if err := json.Unmarshal([]byte(input), &env); err != nil {
        fmt.Print(err)
    }

    display := newDisplay()
    go display.run()
    events(display, event, env.Type)
}

func events(display *Display, raw json.RawMessage, event string) {
    switch event {
    case "hello":
        hello := EventHello{}
        if err := json.Unmarshal(raw, &hello); err != nil {
            fmt.Println(err)
        } else {
            display.formal <- hello
        }
    case "howdy":
        howdy := EventHowdy{}
        if err := json.Unmarshal(raw, &howdy); err != nil {
            fmt.Println(err)
        } else {
            display.western <- howdy
        }
    default:
        fmt.Println("No event handler")
    }

}

После отмены вызова EventEnvelope фактическое событие остается в качестве RawMessage. Событие RawMessage затем демаршалируется в указанный тип c. Может ли это быть Dynami c? Только если не было ошибок, мы должны отправить его на канал.

        hello := EventHello{}
        if err := json.Unmarshal(raw, &hello); err != nil {
            fmt.Println(err)
        } else {
            display.formal <- hello
        }

Тот же код на игровой площадке: https://play.golang.org/p/WPC8JAFyxgq

1 Ответ

0 голосов
/ 04 февраля 2020

Вы можете использовать фабрику типов:

var types map[string]func() interface{} {"hello":func() interface{} {return &EventHello{}}, 
    "howdy": func() interface{} {return &EventHowdy{}}}

Фабрика может использоваться для создания нового экземпляра типа, используя его имя. Вы можете добавить новые функции к этим типам, чтобы они могли выбирать свои собственные каналы:

type sender interface {
   send(Display)
}

func (e EventHello) send(d Display) {
   d.formal<-e
}

Затем можно написать функцию events для обработки всех возможных типов событий:

func events(display *Display, raw json.RawMessage, event string) {
   evInstance:=types[event]()
   if err := json.Unmarshal(raw, evInstance); err != nil {
       fmt.Println(err)
   } else {
       evInstance.(sender).send(display)
   }
}
...