👀
и в документации четко они изложены
Alexander
кто-нибудь разбирал gorm-gen? как бы с одной стороны оно генерит горм модели. а с другой стороны еще какую-то свою обертку, притом, что она какая-то комплексная и в названии заявлено "A safer orm base on GORM," Что там safer и в чем оно лучше? Я посмотрел, там вообще столько оберток, что я даже не знаю
Denis
все ровно наоборот, чтобы оправдать делание чего-то - надо сказать зачем это _точно_ нужно
Ты не совсем меня понял, я предполагаю, что ты знаешь для чего интерфейсы. Ты говоришь что повальное их использование это ООП ГМ, тогда надо разбирать кейсы которые ты называешь ООП ГМ, чтобы у меня было понимание того что ты имеешь ввиду
Alexander
не совсем понял даже что оно такое делает
Alexander
на вид кажется оно будет прожорливым
👀
Ты не совсем меня понял, я предполагаю, что ты знаешь для чего интерфейсы. Ты говоришь что повальное их использование это ООП ГМ, тогда надо разбирать кейсы которые ты называешь ООП ГМ, чтобы у меня было понимание того что ты имеешь ввиду
ну таких кейсов очень много, особенно среди начинающих, которых в школе или где там сейчас, научили сначала на python, а потом посадили на java - очень тяжело переучивать, занимает около 3-4 лет
👀
это я типа пошутил ООП ГМ - ООП Головного Мозга
👀
неудачная шутка
Denis
это я типа пошутил ООП ГМ - ООП Головного Мозга
Да я понял что имеется ввиду, это от ПГМ=) Просто мне стало интересно что за кейсы такие в которых интерфейсы не нужны. Если мы говорим о https://t.me/golangl/13239, то да, здесь интерфейс лишний. Но мне интересно было поговорить о чем-то конечно более конкретном и серьезном, вот я и спросил
👀
Да я понял что имеется ввиду, это от ПГМ=) Просто мне стало интересно что за кейсы такие в которых интерфейсы не нужны. Если мы говорим о https://t.me/golangl/13239, то да, здесь интерфейс лишний. Но мне интересно было поговорить о чем-то конечно более конкретном и серьезном, вот я и спросил
ну в этом "кейсе" он даже не лишний, он там вредный, потому, что он скрывает от разработчика, который потом придет с этим разбираться то как именно работает этот компонент - так допустимо делать лишь в тех случаях, когда поведение компонента (реакции и все проч.) - ограничены, хорошо документированы и при том - очевидны просто из общих соображений - скажем - основная бизнес-логика проекта, а тут не бизнес-логика - а системный уровень - работа с ботом... делать здесь интерфейсы - это заставить разработчика, который потом будет писать бизнес-логику мучаться с двумя неудачными абстракциями - реальным миром, и этой либой если говорить в целом - то интерфейсы не нужны по умолчанию, за редчайшим исключением, которое описано в документации лучше, чем я бы смог
Denis
ну в этом "кейсе" он даже не лишний, он там вредный, потому, что он скрывает от разработчика, который потом придет с этим разбираться то как именно работает этот компонент - так допустимо делать лишь в тех случаях, когда поведение компонента (реакции и все проч.) - ограничены, хорошо документированы и при том - очевидны просто из общих соображений - скажем - основная бизнес-логика проекта, а тут не бизнес-логика - а системный уровень - работа с ботом... делать здесь интерфейсы - это заставить разработчика, который потом будет писать бизнес-логику мучаться с двумя неудачными абстракциями - реальным миром, и этой либой если говорить в целом - то интерфейсы не нужны по умолчанию, за редчайшим исключением, которое описано в документации лучше, чем я бы смог
А если у тебя 2 бота, один для телеги, второй для дискорда, как тогда реализоваывать контракт посылки сообщений? Логично сделать интерфейс interface SendMessage(){ Send(string) } И оба бота будут имплементировать этот интерфейс.
👀
тяжело этот пример обсуждать, я не уверен каков формат сообщений у dicord, но в случае с telegram + whatsapp + internal клиентом я сделал иначе, и не пожалел ни разу
👀
но случай, может быть валидный, но на мой вкус суета не стоит свечь
Denis
тяжело этот пример обсуждать, я не уверен каков формат сообщений у dicord, но в случае с telegram + whatsapp + internal клиентом я сделал иначе, и не пожалел ни разу
Так вот в том то и прикол, что там разные форматы сообщений. Но ты абстрагируешь конкретную реализацию бота, до интерфейса и теперь разработчикам которые хотят просто послать сообщение в любого бога достаточно просто передать сообщение(ну и наверное user_id в рамках системы). А конкретный бот уже сам найдет id чата, токены нужные достанет из базы и пошлет сообщение
Denis
Там выше интерфейс вот так скорее всего должен выглядеть. Где юзер это какая-то наша сущность в системе interface SendMessage(){ Send(User, string) }
👀
если форматы настолько разные, то зачем их пытаться уложить в один тип
👀
если они одинаковые, то почему по контакту которому отправлено сообщение или от которого оно уходит не понимать в своей либе куда отправлять сообщение, в какую сеть
Denis
если форматы настолько разные, то зачем их пытаться уложить в один тип
Ну смотри, мы хотим отправлять уведомления пользователям на 1. Мобилы (смс) 2. Дискорд 3. Ватсап 4. телеграм 5. Емейл 6. Мобилы (через пуш) Логично чтобы все посылалщики реализовывали один интерфейс
👀
ну если тип один - структура одинаковая, то в чем вопрос?
👀
делаешь просто один тип и вперед
Denis
если форматы настолько разные, то зачем их пытаться уложить в один тип
Мы их и не укладываем в один тип, а лишь говорим им какой контракт соблюдать. Контракт это и есть интерфейс. Представь что ты большой бос большой корпорации. Ты пишешь на листке: "Отправить сообщение Васе через телеграм", даешь лист секретарше, а она сама уже идет в телеграм, регается там, ищет васю и отправляет сообщение. Секретарша в данном случае принимает от тебя интерфейс бланка
Denis
ну если тип один - структура одинаковая, то в чем вопрос?
Ну конечно разные, веб пушы это одна реализация, смс другая, телега третья
👀
вопрос только в выборе того через какую сеть отправлять сообщение, верно?
👀
но информация о том, в какую сеть его отправить - доступна, так как известен адресат
👀
а если Вы ведете к тому, что можно так сделать 5 разных либ, разными разработчиками и т.д., в разных проектах - то это слишком стремно, чтобы обсуждать на ночь
Denis
но информация о том, в какую сеть его отправить - доступна, так как известен адресат
Ну юзер id тебе доступен, который у нас в базе лежит, но ID чата и т.п. внутреняя реализация нет. Код бота телеги например может достать эту инфу из базы если надо
Denis
Да. А откуда мы знаем куда отправлять?
В примере с офисом, бос пишет только имя(юзер айди) и соц сеть, а секретарша уже сама работает с соц сетью Бос не сообщает ей номер Васи в телеграмме
👀
type UnifiedMessage struct { Text string Media []*MediaFiles Entities []*Entities Author *UnifiedContact Receivers []*UnifiedContact } type UnifiedContact struct { Network NetworkType NetworkContactId uint64 }
Denis
В примере с офисом, бос пишет только имя(юзер айди) и соц сеть, а секретарша уже сама работает с соц сетью Бос не сообщает ей номер Васи в телеграмме
Все верно. А теперь мы хотим еще проверить перед отправкой для каждого пункта назначений есть ли пользователь в данной соц сети. Что-то типо provider.МожноЛиОтправитьСообщениеПользователю(UnifiedContact) provider - это либо телеграм, либо ватсап и т.п. Как такое реализуешь?
👀
Все верно. А теперь мы хотим еще проверить перед отправкой для каждого пункта назначений есть ли пользователь в данной соц сети. Что-то типо provider.МожноЛиОтправитьСообщениеПользователю(UnifiedContact) provider - это либо телеграм, либо ватсап и т.п. Как такое реализуешь?
type UnifiedMessage struct { Text string Media []*MediaFiles Entities []*Entities Author *UnifiedContact Receivers []*UnifiedContact } type ContactPriority uint8 type UnifiedContact struct { OurDbId uint64 Contacts map[ContactPriority][]*NetworkContact } type NetworkContact struct { Network NetworkType NetworkContactId uint64 }
Denis
type UnifiedMessage struct { Text string Media []*MediaFiles Entities []*Entities Author *UnifiedContact Receivers []*UnifiedContact } type ContactPriority uint8 type UnifiedContact struct { OurDbId uint64 Contacts map[ContactPriority][]*NetworkContact } type NetworkContact struct { Network NetworkType NetworkContactId uint64 }
Ладно, в общем я напишу просто что я имел ввиду: Есть у тебя такой код: chatId:= getChatIdFrom(OurDbId) telegram.SendMessage(chatId, message) phone:= getPhoneFrom(OurDbId) whatsapp.SendMessage(phone, message) token:= getTokenFrom(OurDbId) android.SendPush(token, message) Ты можешь просто сделать интерфейс interface SendProvider { Send(OurDbUser, Message) } Твой код превратится в такой telegram.Send(OurDbUser, message) whatsapp.Send(OurDbUser, message) android.Send(OurDbUser, message) Логика получаения chatId, token, phone будет инкапсулирована внутри конкретных реализаций (telegram,whatsapp и т.п.) При этом если ты хочешь отправить сообщение в несколько соц сетей, то теперь тебе достаточно написать func SendTo(OurUser, message, providers []SendProvider) { for range providers { p.SendMessage(OurUser, message) } } При этом код выше решает какие провайдеры передавать в SendTo в зависимости от бизнес логики. До того как ты ввел интерфейс чтобы послать сообщение в несколько соц сеток по условиям было сложно. Но с вводом интерфейса в системе появился контракт отправки сообщений (SendProvider) и стали скрыты детали реализации доставки сообщений
👀
ну то есть есть какой-то невероятный edge case в системе, где надо внезапно отправить сообщение только в телеграм, а всем остальным никогда и ни по чем в этом месте отправлять сообщение не надо в этом конкретном месте, поэтому делаем hardcode?
👀
ну даже если такое представить, то я бы сделал проверку, что есть telegramContact и какой-нибудь func SendMessageToSpecificNetworkOrError(*UnifiedMessage, NetworkType) error
Denis
ну то есть есть какой-то невероятный edge case в системе, где надо внезапно отправить сообщение только в телеграм, а всем остальным никогда и ни по чем в этом месте отправлять сообщение не надо в этом конкретном месте, поэтому делаем hardcode?
Ну например тебе надо отправить сообщения только в те соц сети в которых зареган пользователь, зачем отправлять туда где он не зареган? 1 Вася зареган в Телеграме и Ватсапе 2. Коля имеет только мобилку с смс и электронную почту
👀
а проблема этого подхода в данном случае на самом деле в том, что на разработчика бизнес-логики (отправка сообщения пользователю системы) перекладывается зачем-то принятие решение о том, через что связываться с пользователем - это плохо, они эту логику будут писать по поводу и без повода
👀
и даже в средних размеров системе - застрелишься потом это все приводить в норму
👀
вот неплохой use-case интерфейсов, как Вам хочется - в стандартной библиотеке в image
Denis
если он там не зареган, то данных не будет по этой регистрации
Кто будет получать информацию о том зареган он или нет?
👀
Кому пренадлежит метод SendMessageToSpecificNetworkOrError?
единственной библиотеке отправки сообщений о которой знает разработчик бизнес-логики
👀
Кто будет получать информацию о том зареган он или нет?
ну Вы говорите, что при таком подходе будет отправляться сообщение туда, где получатель сообщения не зареган, я же говорю - каким образом, если у него нет контакта соотв. типа?
👀
надо убрать отправку в эту сеть, даже если есть контакт - ставим Priority == DontUseEver
👀
или что там еще требуется
👀
просто и понятно
👀
и весь контекст в одном месте, да еще и так, что посмотреть можно - красота же
👀
какой код?
👀
который делает что именно?
Denis
какой код?
Priority == DontUseEver
👀
внутри?
Denis
код который двигает UnifiedContact.Contacts ?
Код который не шлет мне уведомление по email по объявлению, если я выключил туда отправку, вот со скрина выше
👀
шлет сообщение один единственный метод из библиотеки, где определены типы описанные выше
👀
настройку Contacts map[ContactPriority][]*NetworkContact где делать?
👀
или в чем вопрос?
👀
непосредственно структуру, наверное, там же где она возникает из базы и заполняется - где-то в той стороне
Denis
непосредственно структуру, наверное, там же где она возникает из базы и заполняется - где-то в той стороне
Ладно, забей. В Image нормальный пример интерфейса. Там как раз тот кейс о котором я говорю. Любая картинка может быть представлена простым интерфейсом Image. А конкретные реализации под разные форматы PNG, JPG это уже классы которые этот интерфейс реализуют. Так же и с сендером который я описывал выше. Любой отправщик сообщений реализует interface SendProvider { Send(OurDbUser, Message) } А конктреные реализации отправки уже лежат в классах которые реализуют этот интерфейс (telegram, whatsapp, sms) Таким образом вызывающий код может работать с любой картинкой как с Image, а также может работать с любым провайдером отправки как с SendProvider и ему не надо знать деталей реализации конктреных форматов картинок или отправщиков.
👀
но там не было выбора
👀
поскольку надо было дать доступ потребителю библиотеки ко внутренним структурам, которые очень разные
👀
а надо уметь понять что там, обработать по-разному и т.д.
👀
ну то есть можно, конечно, но это приведет к общему ухудшению качества кода в проекте, в котором это используется
Denis
зачем? одинаковое же все
Разные api же, в телеге один, в смс другой и т.д.
👀
👀
потому что вместо switch по типу контакта, надо будет при отправке сообщения писать везде просто Send()
👀
и забирать пользователя тоже унифицировано