👀
👀
и в документации четко они изложены
Alexander
кто-нибудь разбирал gorm-gen? как бы с одной стороны оно генерит горм модели. а с другой стороны еще какую-то свою обертку, притом, что она какая-то комплексная и в названии заявлено "A safer orm base on GORM,"
Что там safer и в чем оно лучше? Я посмотрел, там вообще столько оберток, что я даже не знаю
Alexander
не совсем понял даже что оно такое делает
Alexander
на вид кажется оно будет прожорливым
👀
👀
это я типа пошутил ООП ГМ - ООП Головного Мозга
👀
неудачная шутка
Denis
это я типа пошутил ООП ГМ - ООП Головного Мозга
Да я понял что имеется ввиду, это от ПГМ=)
Просто мне стало интересно что за кейсы такие в которых интерфейсы не нужны.
Если мы говорим о https://t.me/golangl/13239, то да, здесь интерфейс лишний.
Но мне интересно было поговорить о чем-то конечно более конкретном и серьезном, вот я и спросил
👀
Да я понял что имеется ввиду, это от ПГМ=)
Просто мне стало интересно что за кейсы такие в которых интерфейсы не нужны.
Если мы говорим о https://t.me/golangl/13239, то да, здесь интерфейс лишний.
Но мне интересно было поговорить о чем-то конечно более конкретном и серьезном, вот я и спросил
ну в этом "кейсе" он даже не лишний, он там вредный, потому, что он скрывает от разработчика, который потом придет с этим разбираться то как именно работает этот компонент - так допустимо делать лишь в тех случаях, когда поведение компонента (реакции и все проч.) - ограничены, хорошо документированы и при том - очевидны просто из общих соображений - скажем - основная бизнес-логика проекта, а тут не бизнес-логика - а системный уровень - работа с ботом... делать здесь интерфейсы - это заставить разработчика, который потом будет писать бизнес-логику мучаться с двумя неудачными абстракциями - реальным миром, и этой либой
если говорить в целом - то интерфейсы не нужны по умолчанию, за редчайшим исключением, которое описано в документации лучше, чем я бы смог
Denis
ну в этом "кейсе" он даже не лишний, он там вредный, потому, что он скрывает от разработчика, который потом придет с этим разбираться то как именно работает этот компонент - так допустимо делать лишь в тех случаях, когда поведение компонента (реакции и все проч.) - ограничены, хорошо документированы и при том - очевидны просто из общих соображений - скажем - основная бизнес-логика проекта, а тут не бизнес-логика - а системный уровень - работа с ботом... делать здесь интерфейсы - это заставить разработчика, который потом будет писать бизнес-логику мучаться с двумя неудачными абстракциями - реальным миром, и этой либой
если говорить в целом - то интерфейсы не нужны по умолчанию, за редчайшим исключением, которое описано в документации лучше, чем я бы смог
А если у тебя 2 бота, один для телеги, второй для дискорда, как тогда реализоваывать контракт посылки сообщений?
Логично сделать интерфейс
interface SendMessage(){
Send(string)
}
И оба бота будут имплементировать этот интерфейс.
👀
тяжело этот пример обсуждать, я не уверен каков формат сообщений у dicord, но в случае с telegram + whatsapp + internal клиентом я сделал иначе, и не пожалел ни разу
👀
но случай, может быть валидный, но на мой вкус суета не стоит свечь
Denis
Там выше интерфейс вот так скорее всего должен выглядеть. Где юзер это какая-то наша сущность в системе
interface SendMessage(){
Send(User, string)
}
👀
если форматы настолько разные, то зачем их пытаться уложить в один тип
👀
если они одинаковые, то почему по контакту которому отправлено сообщение или от которого оно уходит не понимать в своей либе куда отправлять сообщение, в какую сеть
👀
ну если тип один - структура одинаковая, то в чем вопрос?
👀
делаешь просто один тип и вперед
Denis
если форматы настолько разные, то зачем их пытаться уложить в один тип
Мы их и не укладываем в один тип, а лишь говорим им какой контракт соблюдать. Контракт это и есть интерфейс.
Представь что ты большой бос большой корпорации. Ты пишешь на листке: "Отправить сообщение Васе через телеграм", даешь лист секретарше, а она сама уже идет в телеграм, регается там, ищет васю и отправляет сообщение. Секретарша в данном случае принимает от тебя интерфейс бланка
👀
👀
вопрос только в выборе того через какую сеть отправлять сообщение, верно?
👀
но информация о том, в какую сеть его отправить - доступна, так как известен адресат
👀
а если Вы ведете к тому, что можно так сделать 5 разных либ, разными разработчиками и т.д., в разных проектах - то это слишком стремно, чтобы обсуждать на ночь
Denis
👀
Denis
Denis
Да. А откуда мы знаем куда отправлять?
В примере с офисом, бос пишет только имя(юзер айди) и соц сеть, а секретарша уже сама работает с соц сетью
Бос не сообщает ей номер Васи в телеграмме
👀
type UnifiedMessage struct {
Text string
Media []*MediaFiles
Entities []*Entities
Author *UnifiedContact
Receivers []*UnifiedContact
}
type UnifiedContact 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
👀
👀
а проблема этого подхода в данном случае на самом деле в том, что на разработчика бизнес-логики (отправка сообщения пользователю системы) перекладывается зачем-то принятие решение о том, через что связываться с пользователем - это плохо, они эту логику будут писать по поводу и без повода
👀
и даже в средних размеров системе - застрелишься потом это все приводить в норму
Denis
👀
вот неплохой use-case интерфейсов, как Вам хочется - в стандартной библиотеке в image
Denis
Denis
Denis
👀
👀
надо убрать отправку в эту сеть, даже если есть контакт - ставим Priority == DontUseEver
👀
или что там еще требуется
👀
просто и понятно
👀
и весь контекст в одном месте, да еще и так, что посмотреть можно - красота же
Denis
👀
какой код?
👀
который делает что именно?
👀
👀
внутри?
👀
шлет сообщение один единственный метод из библиотеки, где определены типы описанные выше
👀
настройку Contacts map[ContactPriority][]*NetworkContact где делать?
👀
или в чем вопрос?
Denis
👀
непосредственно структуру, наверное, там же где она возникает из базы и заполняется - где-то в той стороне
Denis
непосредственно структуру, наверное, там же где она возникает из базы и заполняется - где-то в той стороне
Ладно, забей.
В Image нормальный пример интерфейса. Там как раз тот кейс о котором я говорю.
Любая картинка может быть представлена простым интерфейсом Image. А конкретные реализации под разные форматы PNG, JPG это уже классы которые этот интерфейс реализуют.
Так же и с сендером который я описывал выше. Любой отправщик сообщений реализует
interface SendProvider { Send(OurDbUser, Message) }
А конктреные реализации отправки уже лежат в классах которые реализуют этот интерфейс (telegram, whatsapp, sms)
Таким образом вызывающий код может работать с любой картинкой как с Image, а также может работать с любым провайдером отправки как с
SendProvider
и ему не надо знать деталей реализации конктреных форматов картинок или отправщиков.
👀
👀
но там не было выбора
👀
поскольку надо было дать доступ потребителю библиотеки ко внутренним структурам, которые очень разные
Denis
👀
а надо уметь понять что там, обработать по-разному и т.д.
👀
👀
ну то есть можно, конечно, но это приведет к общему ухудшению качества кода в проекте, в котором это используется
Denis
👀
👀
👀
потому что вместо switch по типу контакта, надо будет при отправке сообщения писать везде просто Send()
👀
и забирать пользователя тоже унифицировано