Pull to refresh
8K+
6
Петер@TeaDove

Golang&Python backend developer

31
Rating
Habr Career
Send message

Да, в данном случае если UserService сам создает кеш, а при деструкции закрывает его - это композиция
А если UserService получает кеш из конструктора и никак им не управляет - это агрегация

В данной статье, скорее, вместо слова "композиция" стоило бы использовать "агрегация", мой косяк :(

Вы совершенно правы!

Прощу прощения, оказывается, я все это время путал агрегацию и композицию. В гоферском комьюнити, по моим ощущениям, термин "агрегация" почти не используется, видимо из-за этого конфуз и произошел.

При этом гоферы, опять же по моим наблюдениям, делят так:

type UserService struct{
    Cache // embedding
}
type UserService struct{
    cache Cache // composition
}


При этом акцента на управление не ставится, не важно, создает ли UserService объект cache или получает при конструкции - и то и то называется композицией.
При этом, чисто технически, эмбеддинг является подвидом композиции, просто потому что это не наследование, а именно встраивание одного типа в другой
В обоих случаях UserService имеет некое поле типа Cache, а не является поддипом Cache, как было бы с наследованием
То есть я противопоставляю композицию и наследование, примерно как в этом видосе https://youtu.be/hxGOiiR9ZKg?si=gbHP_KpaFtAHJU4J

Зачем искать ошибки по типам, если можно по стектрейсу, он разве не удобнее?
Собственно крайне много проектов, тот же k8s и cockroachDB используют ошибки с стактрейсом и все ок

Зато как весело и спорно получится, столько срачей в комментах будет :)

А если серьезно, то саму культуру разработки вокруг раста разбирать одно удовольствие, есть же как культисты, что воют за переписывание всего на раст, так и культисты, что борятся против культа раста :)

Да, в случае интерфейсов такое будет

А в случае НЕ интерфейсов в Го гарантировано передастсо именно то, что принимается, а не дочка

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

Под классами я имел ввиду в Го, конечно, структуры, их тут часто называют классами, потому что и так понятно о чем речь (и методы, кстати, у структур есть)

Приведите, пожалуйста, реальную ситуацию, не изолированный воображаемый пример, когда наследование однозначно выгоднее

Прелесть Го в том, что никто не передаст в функцию непойми что, что не нужно разбираться какая функция в реальности вызывается у класса и что кто-то в дочернем классе переопределил её на бог знает что

Я скорее к тому, что Go де-факто конкурирует с Python, Java и JS в сфере бекенд разработки при этом в одинаковых задачах.

Вот, например, нужно вам написать бекенд для нового интернет магазина, к вам придет CEO и будет докапывать, а что лучше использовать?
И вроде Java и Go по перформансу будут похожи, но в джаве магии больше, из-за чего что-то сложное сделать просто, но зато и что-то простое сделать сложно тоже легко. А в Go наоборот, и сложное и просто делается одинаково просто, но долго. (Грубо говоря)
А еще в дискуссию можно запихнуть Python и JS с TS и тоже сидеть гадать что лучше использовать

Есть классный контраргумент против наследования


А зачем вам вообще может понадобиться сразу отсортированный список везде? Что это за реальная задача, где это нужно?

Почему бы ни создать кастомный класс SortedList c методами Get(i int) T, Append(v T) и тд и использовать его?
А в случае (практически невозможном в реальности), когда вам вдруг понадобится в одном проекте в одном наборе функций использовать и SortedList и обычный List - можно использовать те же интерфейсы.

И при каждом изменении интерфейса вы должны менять все где он упоминается и реализован.

Так это всегда нужно делать, если вы меняете интерфейс, то все функции, что используют этот интерфейс должны быть изменены. Но с наследованием же такая же история, если вы в List переименуете метод Get на GetValue, то вам везде придется поменять названия

Прелесть композиции и запрета наследования в Go в том, что Go гарантирует, что если у вашей функции есть 3 аргумента - структура, int и массив интов, то туда можно передать только структуру, int и массив интов, никаких дочерей, пасынков, родственников и друзей, только эти типы данных

  1. Встраивание Кеша в UserService как раз пример композиции

  2. Тоталитарность Го тут проявляется в том, что в питоне есть 5 способов, как можно сделать одну задачу. В данном примере можно протоколы использовать, а можно асбтрактные классы. Один разраб одно будет использовать, другой другое. А в Го - только интерфейсы, даже если интерфейсы тут хуже, чем абстрактные классы

У наследования есть прекрасная не copy-paste альтернатива - композиция :)

Так Rust, Python и Java используют часто в тех же задачах, что и Go

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

Так я, автор, и не говорю, что Go это Грааль или идеал, а просто хороший инструмент под конкретную задачу

(надо было секцию "Минусы" побольше описать...)

О основной фишки голэнга - тоталитарности, что ограничивает возможности разработчика

Я не писал о том, что Го надо использовать везде и ко всему или что его дизайн самый лучший, я писал о том, что сам Го уникален не горутинами, а ограничениями

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

Согласен, собственно поэтому Go и используют только в бекенде и инфраструктуре бекенда, а в геймдеве, фронте, мобилке, системной разработке и тд его почти никогда не используют

Go как раз заставляет, запрещает и дистимулирует

Можно ли работать с ошибками через panic? Можно, но это очень не удобно, потому что в std и сторонних библиотеках принято использовать error, получится смешение подходов, которые еще нужно как-то соединять

Можно ли сихронизировать горутины через классические мьютексы? Да, но зачем, если есть sync.Mutex?

Можно ли эмулировать наследование через embedding? И да и нет, полноценного наследования все-равно не получится. Методы и поля через embedding невозможно переопределить так

Возможно набор небогатый, но достаточный)

Согласен, в этом и суть! Есть все, что нужно для бекенд разработки, ни больше ни меньше. В этом и суть "тоталитарности", дать разрабу достаточно функциональности, чтобы он мог эффективно писать код, а также чтобы он не изобретал велосипед, при этом не слишком много фичей, чтобы он не было зоопарка реализаций

Почему не бывает дополнительных защит? Я только что привел пример дополнительной защиты

Так я и не решаю проблему безопасности через гуиды, а строю дополнительную защиту

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

Упрощать корпоративный шпионаж для конкурентов, как мне кажется, не стоит

Плюс это дополнительная защита

Вдруг окажется, что ваш эндпоинт GET /orders/<ID> не защищен и любой может получить заказ. С UUID такая бреж в безопасности будет, конечно, критичной, но заметно менее критичной, чем с Serial

Но множественное число наталкиваем в принципе на ошибки. Не всё слова тривиально плюрализируются (parking, aircraft, like), а также не совсем очевидно что делать с таблицами типа user_subscription -users_subscriptions? А если у юзера только одна подписка может быть, то кто-то может додуматься и сделать users_subscrition

Большой плюс единственного числа - оно просто проще (за исключением резервированных таблиц)

В одной компании мы отключали FK просто чтобы меньше БД нагружать (они всё-таки не бесплатные)

В другой FK были в локальных таблицах, но между кластерами БДшек не было FK, потому что дорого и это были разные отделы, не факт что у них вообще постгрес была:)

Information

Rating
299-th
Registered
Activity

Specialization

Бэкенд разработчик
Старший
SQL
Golang
Python