Да, в данном случае если 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 и массив интов, никаких дочерей, пасынков, родственников и друзей, только эти типы данных
Встраивание Кеша в UserService как раз пример композиции
Тоталитарность Го тут проявляется в том, что в питоне есть 5 способов, как можно сделать одну задачу. В данном примере можно протоколы использовать, а можно асбтрактные классы. Один разраб одно будет использовать, другой другое. А в Го - только интерфейсы, даже если интерфейсы тут хуже, чем абстрактные классы
О основной фишки голэнга - тоталитарности, что ограничивает возможности разработчика
Я не писал о том, что Го надо использовать везде и ко всему или что его дизайн самый лучший, я писал о том, что сам Го уникален не горутинами, а ограничениями
И это не плюс или минус, это как функциональшина хаскеля или боровчекер в расте - фишка языка, которая определяет его и его использование
Согласен, собственно поэтому 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, потому что дорого и это были разные отделы, не факт что у них вообще постгрес была:)
Да, в данном случае если UserService сам создает кеш, а при деструкции закрывает его - это композиция
А если UserService получает кеш из конструктора и никак им не управляет - это агрегация
В данной статье, скорее, вместо слова "композиция" стоило бы использовать "агрегация", мой косяк :(
Вы совершенно правы!Прощу прощения, оказывается, я все это время путал агрегацию и композицию. В гоферском комьюнити, по моим ощущениям, термин "агрегация" почти не используется, видимо из-за этого конфуз и произошел.
При этом гоферы, опять же по моим наблюдениям, делят так:
При этом акцента на управление не ставится, не важно, создает ли 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 и массив интов, никаких дочерей, пасынков, родственников и друзей, только эти типы данных
Встраивание Кеша в UserService как раз пример композиции
Тоталитарность Го тут проявляется в том, что в питоне есть 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, потому что дорого и это были разные отделы, не факт что у них вообще постгрес была:)