иногда приходится распаралелить запись в канал и придумывать сценарий , где только одна горутина его закроет.
Вот так вот не надо, не надо сценариев, когда один из писателей закрывает канал. И придумывать такие сценарии тоже не надо, все придумано до нас: sync.WaitGroup - канал должен закрываться тогда и только тогда, когда мы гарантированно не собираемся в него больше писать
К сожалению, набор del, home, end, pgup, pgdown и prnscr вплотную справа от шифта и ентера намекает, что опять для осьминогов каких-то делали. А подсветка клавиш говорит о том, что у целевой аудитории ещё и зрение специфически устроено, не так, как у людей.
Честно говоря, ваша позиция выглядит достаточно наивно. Что значит "человек уже прошел собесы, то лучше бы его взять". Собес - это не ЕГЭ и даже не викторина, по результатам которой положен приз.
Сам факт собеседования ещё ни о чем не говорит. "Не можем взять по юридическим причинам" или "остальной команде не понравился" - вполне себе доводы. Вы ж не скакуна для ставок выбираете, а ищете человека, с которым вам потом работать
Don’t refer to volatile concrete classes. Refer to abstract interfaces instead.
Но он же говорит "не ссылайтесь на хрупкие конкретные реализации классов".
to manage this undesirable dependency
И про "нежелательные зависимости".
Вот у вас, например, есть класс, считающий отчетность по договорам за период. Очевидно, что для того, чтобы оную отчетность посчитать, договора надо сначала откуда-то получить. Поэтому у класса есть зависимость - источник данных.
Можно напрямую отдать внутрь обертку над БД, откуда и брать договора тупым SQL-запросом. Ну вот Мартин говорит, что это не самая умная стратегия.
Закройте базу каким-нибудь простеньким репозиторием, и его интерфейс отдайте в ваш класс отчетности. Как минимум, это позволит (уже прямо здесь и сейчас) покрыть логику класса юнит-тестами.
При этом Мартин оперирует термином юнита (да, в классическом ООП оно почти 1-в-1 ложится на границы класса), т.е. акцентирует внимание на публичном интерфейсе. Вот эти правила - они прежде всего о публичной части класса, во внутренней реализации любую дичь творить можно.
DIP - это про то, чтобы нужный экземпляр зависимости не инстанцировался внутри класса, а передавался уже готовый в него снаружи. Ведь, в самом деле, для того, чтобы какой-то класс, работающий с БД, смог установить соединение с оной, нужно, чтобы этот класс знал, что это БД, оперировал такими понятиями как параметры подключения и т.д. и т.п. DIP - про то, чтобы класс бизнес-логики не знал ничего о том, как устроена зависимость, к которой он обращается.
and generally enforces the use of Abstract Factories.
А вот это - один из самых широкоиспользуемых приемов того, как добиться того, чтобы класс, зависящий от данных в БД, не имел никакого понятия о том, что там БД вообще какая-то есть внутри.
Зачем описывать в системе типов ограничения данных, которые в терминологии этих типов описываются (тут, вроде, есть разумная аргументация)?
Зачем НЕ описывать в системе типов ограничения данных, которые в терминологии этих типов описываются (и вот тут, кроме "мы экономим буквы" - пока аргументации не видно)
А судмедэксперты вообще были до 1990-х годов? Вы думаете тогда были морги?
Думаю, морги были. Аж с 1964 года были, например. Я около одного из них в песочнице играл.
А судмедэкспертиза в СССР с 1918 года официально была. И даже в царской России была, даже кафедры в университетах соответствующие водились с середины 19 века....
Например модели обычно делают без внешних зависимостей, моки в таких случаях нужны только для DI внешних сервисов
Ну ведь это именно то, о чем говорит D из SOLID. Осуждаемая тут максима "все-все-все закрывать интерфейсами и абстрактными реализациями" - она же не из SOLID'а, а из головы осуждающих. SOLID буквально рекомендует использовать DI для инъекции внешних зависимостей.
А непосредственно модели данных никто в здравом уме и не предлагал абстрагировать. Нету про это в SOLID ничего.
Хоть ссылки на функции (как бы они ни были сделаны) позволяет?
Нет( Ни функций первого порядка, ни чего-то похожего на указатели на функции, ни условных импортов, ни манки-патчинга. Вообще ничего... Я вот про это и говорю, что язык беден (внезапно, являясь достаточно приличным DSL для своих задач), ничего интересного в нем сделать не получится, тот же SOLID не описываем в терминах языка.
Понимаете, SOLID - это такая мнемоника, сокращенная запись и напоминалка того, о чем в книжке "Чистая Архитектура" написано. В целом, без предварительного прочтения книжки и разбора примеров/ситуаций, вероятно, она и бесполезна.
Описанные в книжке принципы написания поддерживаемого кода разумны и действительно полезны. Чтобы не следовать этим разумным советам, нужна веская причина. Например, "хак для оптимизации производительности" или что-то вроде. Ну, как минимум, сформулировать причину, "почему SOLID тут не нужен" - стоит.
в подавляющем большинстве случаев, по моей практике, оказывается, что направление абстрагирования было выбрано неверно
Вот тут вы не совсем честно сформулировали эту самую причину. То, что вы описали - это "мы не собираемся поддерживать этот код". На самом деле - тоже вполне себе разумная причина не использовать правила написания поддерживаемого кода. Если вы решили, что в случае чего проще будет выкинуть и переписать - почему бы и нет, тут вам SOLID и не нужен, в целом.
Интерфейсы - это инструмент, который нужно использовать не просто чтобы было, а по какой то причине. Тесты - возможная причина. DI - тоже. Но все равно речь не о том, чтобы по умолчанию делать всему интерфейсы.
Но погодите, весь критичный код в идеале должен быть покрыт тестами. Что приводит нас к забавному выводу, что, раз "тесты = интерфейсы" (не настолько грубо и в лоб, но, тем не менее, связь есть), то весь критичный код должен принимать интерфейсы...
Я видел и даже имел несчастье сопровождать объектную систему с виртуальными функциями на bash. При отсутствии в нём пользовательских типов.
Абстрагируемся от вопроса "зачем", хотя про "как" - с удовольствием почитал бы статью от вас (видимо, эрзац-виртуальными функциями выступал импорт из соседних скриптов по условию? или переопределяли функции на лету по условиям?)
Что забавно, встроенный язык 1С не позволяет даже таких "шалостей", типа условных импортов или переопределений. И функций первого порядка он тоже не позволяет. На выходе все реализации "ООП в 1С", которые я видел, сводились к префиксу у методов по типу РозничныйЗаказ_ПосчитатьСтоимость(Заказ) vs ОптовыйЗаказ_ПосчитатьСтоимость(Заказ).
Ну и про место для SOLID... Не, я понимаю, что-то совсем эзотерическое можно попытаться выдавить из себя и родить, но в терминах языка 1С проблематика SOLID не описывается.
Принцип подстановки Лисков (ввиду отсутствия наследования и даже интерфейсов) выродится в "не передавайте в метод то, для чего он не предназначен", а уж как сформулировать инверсию зависимостей - я вообще не представляю.
Только вот конкретный сборник из 5 правил к этой задаче имеет весьма отдалённое отношение.
Погодите, ну вот же оно:
если мы уверены, что EncryptedFileReader не будет использоваться в контексте, где ожидается поведение базового FileReader
Буковка L напрямую запрещает делать такое.
Вот это "если мы уверены" - это прямой путь к "раз****ь проект на ровном месте".
Если мы уверены, что EncryptedFileReader не будет использоваться в контексте, где ожидается поведение базового FileReader - нефиг от этого FileReader наследоваться. Просто до тех пор, пока будут возникать идеи закопать вот такое в коде (а оно только что возникло), приходится прописные истины оформлять в виде мнемоник типа SOLID.
SOLID задумывались именно как набор эвристик для решения конкретных проблем, а не как незыблемые правила. Роберт Мартин предложил их как способы решения определённых проблем, с которыми он сталкивался в конкретных проектах.
SOLID (Мартин сам об этом пишет) решает ровно одну конкретную проблему: как писать поддерживаемый код. Вся задача SOLID'а - сделать так, чтобы при последующей разработке не было мучительно больно от попыток добавить в проект новую фичу, не раз***в к хренам старые.
Если это простой продуктовый код (а не универсальная библиотека), то зачастую гораздо правильнее абстракции выделять по мере возникновения реальной потребности. Добавился новый тип чего-нибудь (или точно будет в будущем) — увидели что с этим много возни стало — добавили абстракцию.
Для того, чтобы "потом" добавить абстракцию, нужно переписать старый код. Т.е. например, при добавлении нового типа заказа вы буквально зачем-то переписываете старый. SOLID - про то, как "подстелить соломку".
Конечно, вы сейчас скажете, что "а зачем покрывать абстракцией один тип заказа?". Ну, собственно, один покрывать SOLID и не требует. Вопрос в том, когда "пора". Второй появился - уже пора, или еще нет, или еще хватит if'ов? А третий? А на каком этапе "пора" наступает? А точно не будет поздно?
Ведь для того, чтобы распилить логику на несколько реализаций и закрыть абстрактным типом, нужно будет еще и всех потребителей этой логики переписать! И чем сложнее логика внутри, тем сложнее будет рефачить.
Т.е. вся "философия" сводится к элементарному "не откладывай на завтра" + конкретные инструкции с указанием того, что не откладывать. Абстракция сейчас - дешевле, чем рефакторинг потом!
При этом изначальная версия с наследованием, хоть и нарушает LSP, более практична и понятна. В реальных проектах такое решение может быть предпочтительнее, особенно если мы уверены, что EncryptedFileReader не будет использоваться в контексте, где ожидается поведение базового FileReader. Т.е. принцип подстановки Лисков для начала подразумевает, что эта подстановка есть, ну или скорее всего точно будет.
"Вот за это вас и не любят". А вот это "если мы уверены, что EncryptedFileReader не будет использоваться в контексте, где ожидается поведение базового FileReader" - это у нас как-то контроллируется? Оно же не позволит его использовать при попытке сделать плохо?
А, позволит, но мы помним, что так делать нельзя? А через год мы вспомним? А через два мы вообще тут будем? А те, кто придет после нас - в курсе? Мы им не забыли сборник "Принципы разработки нашего проекта: 1001 и 1 нельзя" передать, в котором есть запись "п.467: никогда не используйте EncryptedFileReader в контексте, где ожидается поведение его базового класса"?
Мы общеизвестный сборник ограничений SOLID заменяем на внутренний документ "не используем наследника там, где ожидается поведение базового класса" - мы точно все правильно делаем?
Вот так вот не надо, не надо сценариев, когда один из писателей закрывает канал. И придумывать такие сценарии тоже не надо, все придумано до нас: sync.WaitGroup - канал должен закрываться тогда и только тогда, когда мы гарантированно не собираемся в него больше писать
К сожалению, набор del, home, end, pgup, pgdown и prnscr вплотную справа от шифта и ентера намекает, что опять для осьминогов каких-то делали. А подсветка клавиш говорит о том, что у целевой аудитории ещё и зрение специфически устроено, не так, как у людей.
После utils.Must(..... кусок важный забыли!
if r := recover(); r != nil { // TODO handle error here } - вот этот)
И вместо if err != nil на каждый вызов будем писать if r := recover(); r != nil
Ну спасибо, ну удружил! (на самом деле нет)
Честно говоря, ваша позиция выглядит достаточно наивно. Что значит "человек уже прошел собесы, то лучше бы его взять". Собес - это не ЕГЭ и даже не викторина, по результатам которой положен приз.
Сам факт собеседования ещё ни о чем не говорит. "Не можем взять по юридическим причинам" или "остальной команде не понравился" - вполне себе доводы. Вы ж не скакуна для ставок выбираете, а ищете человека, с которым вам потом работать
На самом деле противоречия с демократией нет. Демократия и приватность - ортогональные понятия. Формально.
Но он же говорит "не ссылайтесь на хрупкие конкретные реализации классов".
И про "нежелательные зависимости".
Вот у вас, например, есть класс, считающий отчетность по договорам за период. Очевидно, что для того, чтобы оную отчетность посчитать, договора надо сначала откуда-то получить. Поэтому у класса есть зависимость - источник данных.
Можно напрямую отдать внутрь обертку над БД, откуда и брать договора тупым SQL-запросом. Ну вот Мартин говорит, что это не самая умная стратегия.
Закройте базу каким-нибудь простеньким репозиторием, и его интерфейс отдайте в ваш класс отчетности. Как минимум, это позволит (уже прямо здесь и сейчас) покрыть логику класса юнит-тестами.
При этом Мартин оперирует термином юнита (да, в классическом ООП оно почти 1-в-1 ложится на границы класса), т.е. акцентирует внимание на публичном интерфейсе. Вот эти правила - они прежде всего о публичной части класса, во внутренней реализации любую дичь творить можно.
DIP - это про то, чтобы нужный экземпляр зависимости не инстанцировался внутри класса, а передавался уже готовый в него снаружи. Ведь, в самом деле, для того, чтобы какой-то класс, работающий с БД, смог установить соединение с оной, нужно, чтобы этот класс знал, что это БД, оперировал такими понятиями как параметры подключения и т.д. и т.п. DIP - про то, чтобы класс бизнес-логики не знал ничего о том, как устроена зависимость, к которой он обращается.
А вот это - один из самых широкоиспользуемых приемов того, как добиться того, чтобы класс, зависящий от данных в БД, не имел никакого понятия о том, что там БД вообще какая-то есть внутри.
Давайте сравним:
id *= w
id *= ID(w)
Явное же лучше неявного. Есть 2 вопроса:
Зачем описывать в системе типов ограничения данных, которые в терминологии этих типов описываются (тут, вроде, есть разумная аргументация)?
Зачем НЕ описывать в системе типов ограничения данных, которые в терминологии этих типов описываются (и вот тут, кроме "мы экономим буквы" - пока аргументации не видно)
Думаю, морги были. Аж с 1964 года были, например. Я около одного из них в песочнице играл.
А судмедэкспертиза в СССР с 1918 года официально была. И даже в царской России была, даже кафедры в университетах соответствующие водились с середины 19 века....
Дык, смертность при первых родах - это как раз про 15-19.
Весьма вероятно, что это от несовершенства диагностики
Ну ведь это именно то, о чем говорит D из SOLID. Осуждаемая тут максима "все-все-все закрывать интерфейсами и абстрактными реализациями" - она же не из SOLID'а, а из головы осуждающих. SOLID буквально рекомендует использовать DI для инъекции внешних зависимостей.
А непосредственно модели данных никто в здравом уме и не предлагал абстрагировать. Нету про это в SOLID ничего.
Нет( Ни функций первого порядка, ни чего-то похожего на указатели на функции, ни условных импортов, ни манки-патчинга. Вообще ничего... Я вот про это и говорю, что язык беден (внезапно, являясь достаточно приличным DSL для своих задач), ничего интересного в нем сделать не получится, тот же SOLID не описываем в терминах языка.
Понимаете, SOLID - это такая мнемоника, сокращенная запись и напоминалка того, о чем в книжке "Чистая Архитектура" написано. В целом, без предварительного прочтения книжки и разбора примеров/ситуаций, вероятно, она и бесполезна.
Описанные в книжке принципы написания поддерживаемого кода разумны и действительно полезны. Чтобы не следовать этим разумным советам, нужна веская причина. Например, "хак для оптимизации производительности" или что-то вроде. Ну, как минимум, сформулировать причину, "почему SOLID тут не нужен" - стоит.
Вот тут вы не совсем честно сформулировали эту самую причину. То, что вы описали - это "мы не собираемся поддерживать этот код". На самом деле - тоже вполне себе разумная причина не использовать правила написания поддерживаемого кода. Если вы решили, что в случае чего проще будет выкинуть и переписать - почему бы и нет, тут вам SOLID и не нужен, в целом.
Но погодите, весь критичный код в идеале должен быть покрыт тестами. Что приводит нас к забавному выводу, что, раз "тесты = интерфейсы" (не настолько грубо и в лоб, но, тем не менее, связь есть), то весь критичный код должен принимать интерфейсы...
Абстрагируемся от вопроса "зачем", хотя про "как" - с удовольствием почитал бы статью от вас (видимо, эрзац-виртуальными функциями выступал импорт из соседних скриптов по условию? или переопределяли функции на лету по условиям?)
Что забавно, встроенный язык 1С не позволяет даже таких "шалостей", типа условных импортов или переопределений. И функций первого порядка он тоже не позволяет. На выходе все реализации "ООП в 1С", которые я видел, сводились к префиксу у методов по типу РозничныйЗаказ_ПосчитатьСтоимость(Заказ) vs ОптовыйЗаказ_ПосчитатьСтоимость(Заказ).
Ну и про место для SOLID... Не, я понимаю, что-то совсем эзотерическое можно попытаться выдавить из себя и родить, но в терминах языка 1С проблематика SOLID не описывается.
Принцип подстановки Лисков (ввиду отсутствия наследования и даже интерфейсов) выродится в "не передавайте в метод то, для чего он не предназначен", а уж как сформулировать инверсию зависимостей - я вообще не представляю.
Выглядит так, что чинить надо сериализацию/десериализацию. "Чинить" хранение, если сломан маршаллинг - странное решение, имхо.
Погодите, ну вот же оно:
Буковка L напрямую запрещает делать такое.
Вот это "если мы уверены" - это прямой путь к "раз****ь проект на ровном месте".
Если мы уверены, что EncryptedFileReader не будет использоваться в контексте, где ожидается поведение базового FileReader - нефиг от этого FileReader наследоваться. Просто до тех пор, пока будут возникать идеи закопать вот такое в коде (а оно только что возникло), приходится прописные истины оформлять в виде мнемоник типа SOLID.
SOLID (Мартин сам об этом пишет) решает ровно одну конкретную проблему: как писать поддерживаемый код. Вся задача SOLID'а - сделать так, чтобы при последующей разработке не было мучительно больно от попыток добавить в проект новую фичу, не раз***в к хренам старые.
Для того, чтобы "потом" добавить абстракцию, нужно переписать старый код. Т.е. например, при добавлении нового типа заказа вы буквально зачем-то переписываете старый. SOLID - про то, как "подстелить соломку".
Конечно, вы сейчас скажете, что "а зачем покрывать абстракцией один тип заказа?". Ну, собственно, один покрывать SOLID и не требует. Вопрос в том, когда "пора". Второй появился - уже пора, или еще нет, или еще хватит if'ов? А третий? А на каком этапе "пора" наступает? А точно не будет поздно?
Ведь для того, чтобы распилить логику на несколько реализаций и закрыть абстрактным типом, нужно будет еще и всех потребителей этой логики переписать! И чем сложнее логика внутри, тем сложнее будет рефачить.
Т.е. вся "философия" сводится к элементарному "не откладывай на завтра" + конкретные инструкции с указанием того, что не откладывать. Абстракция сейчас - дешевле, чем рефакторинг потом!
"Вот за это вас и не любят". А вот это "если мы уверены, что EncryptedFileReader не будет использоваться в контексте, где ожидается поведение базового FileReader" - это у нас как-то контроллируется? Оно же не позволит его использовать при попытке сделать плохо?
А, позволит, но мы помним, что так делать нельзя? А через год мы вспомним? А через два мы вообще тут будем? А те, кто придет после нас - в курсе? Мы им не забыли сборник "Принципы разработки нашего проекта: 1001 и 1 нельзя" передать, в котором есть запись "п.467: никогда не используйте EncryptedFileReader в контексте, где ожидается поведение его базового класса"?
Мы общеизвестный сборник ограничений SOLID заменяем на внутренний документ "не используем наследника там, где ожидается поведение базового класса" - мы точно все правильно делаем?
Никак. В 1С нет пользовательских типов: https://v8.1c.ru/platforma/sistema-tipov/
Собственно, и SOLID'а там нет.