Pull to refresh

Comments 8

Только начал читать пример с UserID, сразу хотел указать на эту особенность

Возможно создать пустую структуру вне пакета, даже если все поля неэкспортируемые

Вот это было одной из головных болей, когда я писал на го

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

Ну а проверять в каждой функции по типу IzZero - ну уж нет, извините, в пхп насмотрелся на if ($a != null && is_array($a)) и т.д., больше не хочу

Именно из за людей с таким мышлением как в статье код на го в некотрых проектах превратился в нереальную мешанину из папок, файлов, слоев, доменов, hex'ов и прочего мусора который в большинстве случаев является препятствием а не решением реальных проблем.

Не нужно строить город там где достаточно
func (x *X) X(xrequest *XRequest) (xresponse *XResponse) {
x.db.QueryRow("select * from x where x = $1", xrequest).Scan(xresponse)
}

Можете уточнить свой пример? Сейчас выглядит так, будто это какая-то идеальная апишка, которой для ответа не нужно ни рассчитывать дополнительных параметров запроса в БД, ни предобрабатывать переданные, а отдавать можно поля без постобработки. Кажется, апишка в таком виде просуществует примерно нисколько.
Будь всё всегда так просто, статья бы не появилась, мне кажется 😅

у вас в примерах с тегами ` протерялись

Для себя выработал простое правило в разработке - "Не выё..ся". Чем проще и понятнее твой код, тем лучше. Если можно обойтись без сложной иерархии - обходись без нее, если не нужны дженерики - к черту дженерики, если можно обойтись без кодогенерации, то к черту кодогенерацию (тут у меня особые вьетнамские флешбеки, после работы на одном проекте где из-за самописной кодогенерации проект на мощном железе по пол дня пытался собраться и не всегда у него получалось).

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

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

Вот эта фраза для меня стала красным флагом чтобы остановится и не читать дальше. К моему большому сожалению когда презентовали Golang всем сказали буквально следующее:

"Удобно и производительно, быстрое создание микросервисов"

Точнее, это так услышали. Реальность она совсем другая. Если кто переходил в Golang с других языков типа Python/Nodejs и в задаче было ускорение существующей схемы и смог переступить парадигму мышления которую накладывают другие языки скорее всего пришел к многопоточности и побочным проблемам которые она приносит (например Race Condition, работа с Mutex и тд). И эти проблемы заставляют мыслить совсем по другому, делать рефакторинг того что уже сделано...а самое главное, ты начинаешь заглядывать под капот пакетов которые используешь и смотришь их реализацию

Именно поэтому я зацепился за конкретное предложение автора. Если мы создаем условную универсальную модель которую хотим использовать в разных проектах/пакетах (там был пример про Users), то для того чтобы её использовать ты пишешь методы буквально на все. Пример:

type Users struct { ... }

// Можно пойти вот так...добавив еще какую-то валидацию
func (u *Users) SetName(n string){
  u.Name = n
  // Можно еще какую-то логику валидации сделать и обработку ошибок
}

// А еще вот так...для кейсов где требуется инкрементное потокозащищенное изменение
func (u *Users) SetName(n string){
  ChUsers <- Update{Action: "Name", Param: n}
}

// Тут должен быть пример еще с mutex...без него никуда

И тоже самое касается чтения. И для того чтобы это понять надо один раз наступить на многопоточность. И сразу начинаешь понимать ЧТО РЕАЛЬНО ИЗМЕНЯЕТСЯ и как, иначе Golang вам принесет пару неприятных сюрпризов...и никакого прямого доступа к значению по вызову Users.Name ты не делаешь т.к это не безопасно с точки зрения многопоточности. Только методы а-ля func (u *Users) GetName string {...} и тп

Вот такое:

...
	CreatedAt    null.Time   `boil:"created_at" json:"created_at,omitempty" toml:"created_at" yaml:"created_at,omitempty"`
...

Это содомия чистой воды. Давайте рассмотрим ситуацию где к примеру надо перевести на json какую-то общую модель...как делается в больших многопоточных проектах и личное мое мнение – правильный вариант:

Мы создаем отдельный пакет, где инициализируется метод/функция с временной структурой где уже проставляется: json:"created_at,omitempty"
А далее в зависимости от ваших предпочтений заполняется к примеру подобными методами как в примере выше а-ля: "(u *Users) GetName"
Почему так? Потому что универсальная модель которую ты грузишь в различные приложения должна быть без лишнего мусора. Зачем модель с параметрами json в приложении где нет вообще преобразований этих. Зачем этот мусор туда тащить? Golang - это про скорость

В заключение.

Я дочитал статью до конца и не в обиду автору, но все же эти высокие размышления сами по себе отваливаются как только Golang начинает использоваться так, как его задумывали изначально т.е в многопоточном исполнении. И структура "как правильно" нарисуется сама

Это хороший комментарий, но, поправьте меня если я ошибаюсь, автор как раз говорит что единая модель есть результат привратников понятого принципа DRY и лучше отказаться от этой идеи так как вам прийдется впадать в содомию, на которую вы верно указали, а захотев изменить что-то в этой мегасущности вы столкнетесь с проблемами. Автор как раз указывает что User это антипатерн.

Sign up to leave a comment.

Articles