Pull to refresh

Comments 17

UFO just landed and posted this here

Ну про что может быть первый комментарий к статье про Go? Правильно! Про Rust!

Можно через interface{}, но код будет выглядеть более громоздко из-за type assertion..

UFO just landed and posted this here
Что вы называете «суммированим типов»?

В Go вы просто преобразовываете intrface{} (он по сути объединяет вообще все типы языка) в тип который ожидаете и проверяете ошибку этого преобразования. Если без ошибки — значит все ок — пользуйтесь полученным типом данных. Ну а ошибка — думайте как обработать.

Именно из за обработки ошибок преобразований код такого решения довольно сильно разрастается, но иначе и нельзя.
UFO just landed and posted this here
Ну а какая разница сумма-тип упадет при «натягивании на ненатягуемое» или assertion ошибку даст, когда вы из интерфейса попробуете «получить неполучаемое»?
UFO just landed and posted this here
UFO just landed and posted this here
А можно пример кода, как именно предлагается сделать?
UFO just landed and posted this here
Здесь обратная задача, то есть, не в json, а обратно. При этом в условиях задано, что структура неизвестная.
map[string]interface{} — это просто общий интерфейс объекта json. Естественно, что в него декодируется любой объект из json строки.
И да, конечно, переменную типа map[string]interface{} можно создать в коде и потом её кодировать в json, и это было бы решением прямой задачи. Собственно, его код я и спрашивал. Просто интересно, чем конкретно оно лучше. Те решения, что я встречал (через map[string]interface{}), лучшими бы я не назвал.
Спасибо, красивое решение… хотя и пришлось вчитаться несколько раз чтобы понять суть задачи и способ ее решения. По сути gob тут простой пересборщик из большой структуры в меньшую. И должен по идее работать быстро.
Но было бы здорово попробовать решить эту задачу «как-то неправильно» и бенчами прогнать в паре с этим решением.
В чем проблема взять и явно конвертировать исходную обобщённую структуру в нужный набор урезанных и не мудрить с аттрибутами?
В том месте, где вы принимаете решение какое представление внедрить почему просто не вернуть признак желаемого представления и не сделать фабричный метод который вернет нужный вам JSON? Сериализовать в gob, чтобы десериализовать из gob, чтобы сериализовать в JSON… Как-то такие решения должны настораживать их авторов
Если я правильно понял, то Вы предлагаете использовать дополнительный приватный атрибут типа
toJson           func() (data []byte, err error)

И именно в него положить всю логику преобразования в json.
При этом для каждого отображения нужно иметь свою функцию. Теперь вопрос: а что будет в тех функциях? типа такого:

        return json.Marshal(&struct {
            Id               int64  `json:"ref"`
            Title            string `json:"title"`
            Description      string `json:"description"`
            PromoPrice       int64  `json:"price"`
            PromoDescription string `json:"promo,omitempty"`
            Links            Links  `json:"links"`
        }{
            Id:               b.Id,
            Title:            b.Title,
            Description:      b.Description,
            PromoPrice:       b.PromoPrice,
            PromoDescription: b.PromoDescription,
            Links:            b.Links,
        })

Если как по мне, то читабельности и разделения кода становится на порядок меньше, хотя как показывают замеры скорости, это будет быстрее.
Или имелось в виду совсем другое решение?
Про скорость и бенчмарки. (в ответ TonyLorencio, Sly_tom_cat и, кончено, всем, кому интересно).
Главный вопрос тут, конечно, про целесообразность использования gob, и дополнительного кодирования/декодирования, поэтому давайте рассмотрим именно этот момент.
Вообще, решение конечно более архитектурное, направленное на разделение кода, чтобы при сливании git веток меньше приходилось править конфликты, которые бы были неизбежны, если бы всё решение было внутри функции MarshalJSON.
Итак, давайте сделаем тоже самое, но без использования gob, а с прямым заполнением структуры. В коде есть закомментированный текст — о нём будет далее.
book1.go
package models

import (
    "encoding/json"
    "fmt"
    "time"
)

const (
    SiteBook1View = iota
    Partner1Book1View
    Partner2Book1View
    PromoBook1View
)

func (b *Book1) SetDefaultView() {
    b.SetSiteView()
}

func (b *Book1) SetSiteView() {
    b.view = SiteBook1View
}

func (b *Book1) SetPartner1View() {
    b.view = Partner1Book1View
}

func (b *Book1) SetPartner2View() {
    b.view = Partner2Book1View
}

func (b *Book1) SetPromoView() {
    b.view = PromoBook1View
}

type Book1 struct {
    Id               int64
    Title            string
    Description      string
    Partner2Title    string
    Price            int64
    PromoPrice       int64
    PromoDescription string
    Partner1Price    int64
    Partner2Price    int64
    // Links            Links
    UpdatedAt time.Time
    CreatedAt time.Time
    view      int
}

func (b Book1) MarshalJSON() (data []byte, err error) {
    if b.view == 0 {
        b.SetDefaultView()
    }
    switch b.view {
    case SiteBook1View:
        return json.Marshal(SiteBookView{
            Id:          b.Id,
            Title:       b.Title,
            Description: b.Description,
            Price:       b.Price,
            // Links:       b.Links,
        })
    case Partner1Book1View:
        return json.Marshal(Partner1BookView{
            Id:            b.Id,
            Title:         b.Title,
            Partner1Price: b.Partner1Price,
            // Links:         b.Links,
        })
    case Partner2Book1View:
        return json.Marshal(Partner2BookView{
            Id:            b.Id,
            Partner2Title: b.Partner2Title,
            Description:   b.Description,
            Partner2Price: b.Partner2Price,
            // Links:         b.Links,
        })
    case PromoBook1View:
        return json.Marshal(PromoBookView{
            Id:               b.Id,
            Title:            b.Title,
            Description:      b.Description,
            PromoPrice:       b.PromoPrice,
            PromoDescription: b.PromoDescription,
            // Links:            b.Links,
        })
    default:
        err = fmt.Errorf("undefined view")
        return
    }
    return
}




Тест создаёт список из элементов и потом получает json строку. Результаты:
BenchmarkRun0-8 20000 68701 ns/op
BenchmarkRun1-8 200000 7177 ns/op

То есть, использование gob даёт замедление в 10 раз. Да, это несколько больше, чем ожидалось.
Но это простые типы данных: int64 и string. А что будет, если будет что-то посложнее? Давайте добавим список литературы.
Код LInks
type Link struct {
	Title       string
	Description string
	Rate        float64
}

type Links []Link

type Book struct {
	Id               int64
	Title            string
	Description      string
	Partner2Title    string
	Price            int64
	PromoPrice       int64
	PromoDescription string
	Partner1Price    int64
	Partner2Price    int64
	Links            Links
	UpdatedAt time.Time
	CreatedAt time.Time
	view      BookView
}
type SiteBookView struct {
	Id          int64  `json:"sku"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Price       int64  `json:"price"`
	Links       Links  `json:"links"`
}
//к остальным также добавим Links с каким-то json всевдонимом


Пусть всегда будет по списку литературу в 100 позиций. Результаты уже не столь плачевны:
BenchmarkRun0-8 3000 493611 ns/op
BenchmarkRun1-8 5000 322898 ns/op
Замедление уже составляет 52%. При более сложных данных может быть и меньше.
Если вам нужно выводить до 100 элементов, при этом их структура достаточно сложная, то замедление будет не столь критичным. Если это тонны информации, и каждая миллисекунда на счету, то, возможно, то данное решение может быть не очень эффективным.
Да, это плата за разделение кода, уменьшение риска конфликтов при слиянии git веток, наглядность моделей. Возможно, этого всего можно добиться и без gob, или с ним, но другим, более эффективным решением. Если найду — обязательно поделюсь.

Sign up to leave a comment.

Articles

Change theme settings