Поток из канала интерфейсов в срез типа - PullRequest
0 голосов
/ 06 мая 2020

Я реорганизовал некоторый код, в котором я делал запрос для разных типов (Книги, Авторы и т. Д. c), и обнаружил, что делаю много одинаковых шагов для выполнения запроса на разных машинах, поэтому я начал рефакторинг и с использованием замыкания.
Проблема в том, что запросы возвращают разные типы, поэтому как захватить эти данные, а затем вернуть их.

Я закончил потоковую передачу результатов в чан интерфейсов. Оттуда я захватываю их в интерфейсе [], прежде чем, наконец, маршалировать их в конечный фрагмент, например [] * model.Book или [] * model.Author.

То, что я придумал, работает, но он кажется неэффективным, особенно при переходе от канала к срезу интерфейсов к последнему срезу.

Есть ли лучший способ захватить данные из интерфейса chan {} в переменную ответа, переданную в функцию excuteQueries?

Вот базовый c fun c, который выполняет запрос и передает результаты в канал интерфейса, называемый выходом. У меня их около 8 для разных типов, поэтому chan имеет тип interface {}

fn := func(db Database, query string, output chan<- interface{}) {
    response, err := db.Books(query)

    // Convert the data and write to the channel
    for _, n := range response {
        output <- &model.Book{Book: n}
    }
}

Эта функция передается другому фонду, который выполняет итерацию по всем базам данных и выполняет запрос. Передается fun c и фрагмент, который используется для захвата результатов, т.е.

var results []*model.Book
executeQueries(fn, &results) 

В функции executeQueries, которая вызывает 'fn', я создаю выходной канал и получаю go обычное чтение из него в [] интерфейс {}. После закрытия канала мы маршалируем все результаты из interfaceSlice в ответ. например,

typedef QueryFN func(db Database, query string, output chan<- interface{})

func(fn QueryFN, response interface{}) {

    output := make(chan interface{})

    // interfaceSlice is used to drain the channel
    var interfaceSlice []interface{}
    done := make(chan bool) // done channel used to indicate when the processing of the output channel is complete

    go func() {
        for i := range output {
            interfaceSlice = append(interfaceSlice, i)
        }
        done<-true
    }()


    for _, db := range databases {
        // some wait group logic....
        go fn(db, query, output)
    }

    wg.Wait()
    close(output)
    <-done

   marshallResponse(interfaceSlice, response) 

Здесь код маршалла определяет тип ответа, а затем выполняет итерацию по входному срезу, приводя элементы. Это не масштабируется, когда я добавляю больше типов, и кажется неэффективным для go от канала до среза и до ответа.

func marshallResponse(input []interface{}, response interface{}) {
    switch res := response.(type) {
    case *[]*model.Book:
        *res = make([]*model.Book, len(input))
        data := *res
        for i := range input {
            data[i] = input[i].(*model.Book)
        }
        case *[]*model.Author:
        *res = make([]*model.Author, len(input))
        data := *res
        for i := range input {
            data[i] = input[i].(*model.Author)
        }
    }
...