Ну а у меня ощущение, что вы уперлись рогом в свои представления и не слышите собеседника. Ведь только тролль может быть с вами не согласен, правда?
Я пока слышал от вас аргументацию уровня джуниора, и «особое» понимание принципов SOLID.
Из ваших слов получается:
SRP вы воспринимаете как необходимость дробить до безумия,
OCP — запрет на редактирование кода
LSP — это о преобразование сложных типов
ISP — требует интерфейсов из одного метода
Честно я ждал от вас вдумчивых аргументов, тоскуя вечерами. А получается что вы просто не понимаете, не умеете и логично что не любите SOLID и всячески защищаете свою безграмотность.
Ну вот профайлер вам показывает, что создание 100к объектов вместо 10к — это как-то сильно долго. Что вы там соптимизируете, не выпиливая SRP?
Прежде чем «грязнить» код — есть много других возможностей:
1) Большинство проблем происходят из O-сложности. Если O — побеждена — переходим на уровень малой крови.
2) Делаем синглтоны или кэши для часто используемых объектов, меняем ref на value типы (если позволяет инфраструктура) — итд.
3) Если и это не помогло — то часто можно упростить или реорганизовать архитектуру во благо скорости и без ущерба смыслу (на этом этапе уже понятно как).
4) Для высоконагруженных — не забываем про горизонтальное масштабирование.
И только потом, когда все вышеперечисленное не помогло — начинаем крошить код во благо скорости. Но необходимость этого встречается редко. Как правило на мобилках и ARM. И, безусловно — тут все средства хороши.
Что самое главное — предыдущие 4 пункта сложно выполнить, если у вас там все SOLID-ы порушены вкривь и вкось.
Он говорит «интерфейс закрыт для изменения, но открыт для расширения», что прямо противоречит рефакторингу.
Он говорит про изменение поведения, а не про то, что код нельзя трогать. Например — смена конекшн стринги из конфиг файла.
Или более сложный случай — вы хотите сменить одну SQL базу на другую — значит подготовьте бек-код так, что бы можно было менять провайдера DB, а не удалять старого и вместо него писать нового. Так же это говорит о том, что предпочитайте if-чикам — стратегии и композицию
LSP фактически утверждает, что все типы должны быть ковариантны. Что во первых не так. А во вторых даже не является свойством типа.
Lsp утверждает, что наследование и интерфейсы должно быть обоснованно по смыслу, а не для экономии кода. Иными словами оно против структурной типизации в языках — где структурной типизации нет, так как это лютый самострел.
Есть ощущение, что вы принципиально пытаетесь переспорить изначальный тезис, что спортивно и прикольно.
Позвольте вашей фантазии, добавлять к каждому абзацу фразу про «кровавое легаси»:
Не знаю, что такое «атомарные» тесты, но написать код так, чтобы он проходил один тест, но не проходил остальные, обычно сложно и бессмысленно.
«атомарные» — тестирующие минимальный объект. Я имею ввиду, что использование TDD в интеграционном ключе, тоесть тестирование больших кусков кода за раз, не тестируя маленькие — имеет тенденцию к «спецификации макарон в виде тестов». Кажется человечество еще не придумало как распутывать такие клубки.
Дьявол в слове «преждевременная». Архитектурная оптимизация должна выполняется как можно раньше, так как потом менять её будет сложно или даже невозможно. Это, кстати, большое заблуждение, что можно не думать о скорости, пока петух не клюнет.
Архитектурная «оптимизация» это хорошо, но речь про обычную, вызванную SRP. И это не заблуждение, по крайней мере если в стеке есть нормальные инструменты профайлинга. А вот экономия кол-ва типов — это кажется оскорбление чуств верующих в прекрасное, да и будущих разрабов за компанию.
Ко кровавому легаси приводит отсутствие регулярного рефакторинга. А OCP явно запрещает рефакторинг. OCP полезен лишь при взаимодействии со внешними клиентами, когда нужно сохранять обратную совместимость.
Ocp не запрещает и не поощряет рефакторинг. Он говорит что «изменение поведения кода нужно делать через… поведения-)». Иными словами — не надо хардкодить.
Не, вы не поняли суть проблемы. Смотрите, Cat является подтипом Animal, Cat[] является подтипом Animal[]. И если мы только читаем, то мы можем где угодно вместо Animal[] передать Cat[]. Но если мы где-то помещаем в массив, например, Dog, то, внезапно, мы уже не можем передавать Cat[] вместо Animal[]. Во времена Барбары об этой проблеме ещё не думали.
Либо я вас по прежнему не понял, либо вы путаете LSP и проблемы ковариантных/контравариантных преобразований и структурной типизации.
TDD. Про TDD в оригинальном варианте максималиста — я соглашусь с вами. Однако гармоничный путь, описанный в исскустве автономного тестирования — это смесь написания тестов иногда перед, иногда после, но в основном — одновременно с написанием кода. «думай о тестах и коде как о едином целом (с) Тестиус». Потому я и указал TDrivenD/TLastD что бы не нарваться на формализм. Кстати отсутствие атомарных тестов зачастую приводит к кровавому легаси.
SRP. Проблема в понимании SRP как «дроби пока дробится». Но это не так. SRP — он же принцип единой изменчивости, единой ответственности и локализации фичей — говорит про границы декомпозиции, а не про ее размер. Нарушение Srp — это не только God-object, но и размазывание и дублирование по коду какой-то конкретной фичи. Дублирование кода, как вы понимаете отнють не ускоряет даже MVP. Ежели мы касаемся вопросов оптимизации — то «преждевременная оптимизация — зло (с)». Кстати неверная декомпозиция, приводит к костылям в коде и кровавому легаси.
OCP, при нормальном проектировании необходим для выполнения SRP и тестирования. А устаревшие интерфейсы можно просто удалить. Другое дело что вы не сможете их удалить если у вас нарушен OCP, что приведет вас к кровавому легаси.
LSP говорит что наследование и полиморфизм должны быть сделаны с умом, в том числе. Везде где есть нарушение LSP — начинается задница и борьба с иерархиями. Как правило тут начинают говорить про кровавое легаси.
ISP не приводит к интерфейсам из одного метода. Аналогично SRP, ISP говорит о том как нужно делать интерфейсы в спорной ситуации, и позволяет развязать жуткие клубки зависимостей. Это крайне необходимо для больших команд, сложной бизнесс логики и ограниченных контекстов. В противном случае — ну вы поняли ;)
DIP — соглашусь. Есть доказательства того, что слепое следование DIP приводит к рекурсивному генерированию кода. Таким образом DIP это не принцип, а ползунок, пренебрегать которым не стоит ни в одну, ни в другую сторону.
В синтаксическом дереве — набор типов фиксирован стандартом языка.
С другой стороны Visitor это не только паттерн матчинг но и SRP точка. Как раз та самая логическая группировка процедур обработки узлов.
И в довесок к ништякам — Visitor есть очень удобный интерфейс для использования в DFS. Тоесть отлично разделяются алгоритм обхода дерева и алгоритм преобразования.
Также есть вопрос к процедурному обходу — по читабельности:
В «безвизиторном» варианте обхода дерева с огромной вариативностью алгоритмов, вопрос группировки всех этих процедур встает очень остро, и, по сути, решается внимательностью и совестью разработчика. Это ок — если разраб один. Это очень не ок когда разрабов несколько и у каждого немного свое мнение, а среди них еще и затаился человек-снежинка.
Итого. В данном случае, визитор это:
— SRP
— Читабельность
— Строгая типизация
То есть больше, чем просто паттерн матчинг фиксированной иерархии в одном модуле.
Я не очень опытный разрабочик компиляторов, но, кажется для конкретно вашей задачи отлично подходит «Визитор». Безусловно нужно смотреть контекст задачи.
С проблемой каши из кода, при подходе DFS по синтаксическим нодам со свитчами и процедурами я столкнулся уже при N = 7, и M = 30.
Также, у каждого типа синтаксической ноды есть множество принципиально различных атрибутов, нужных для анализа и принятия решений, что есть жирный, статически типизированный плюс (привет Roslyn).
Один такой визитор будет инкапсулировать один из N алгоритмов для всех M нод. В случае дубляжа кода можно воспользоваться как полиморфизмом так и наследованием.
Стабильность нет кора — не ниже джава стека.
Стабильность мини и микросервисной архитектуры — не зависит от того что сервисы на разных платформах
Стоимость разрабов джава — неоправданно выше.
Стоимость разработки на джава — немного выше
Скорость работы стека кора — выше
Вопрос: при выборе какой технологии для нового сервиса (Java или Net) у вас появятся основания для увольнения?
Он уже не «полигонный». Дополнительной тенденцией к переводу является мини и микросервисность современных решений. Стоит один раз человеку написать модельку с 20ю свойствами на C# (после джава) и решение писать новый сервис на коре начинает вызывать теплые, интригующие и по детскому чистые эмоции.
Как хорошо что он туда не попадет. Проприетарная, тяжеловесная, с изотерическими конфигами, толи Фреймворк, толи библиотека. Да, для своего времени это было круто, но теперь есть grpc, да и то, если сможете обосновать почему не использовать http
Я пока слышал от вас аргументацию уровня джуниора, и «особое» понимание принципов SOLID.
Из ваших слов получается:
SRP вы воспринимаете как необходимость дробить до безумия,
OCP — запрет на редактирование кода
LSP — это о преобразование сложных типов
ISP — требует интерфейсов из одного метода
Честно я ждал от вас вдумчивых аргументов, тоскуя вечерами. А получается что вы просто не понимаете, не умеете и логично что не любите SOLID и всячески защищаете свою безграмотность.
Это вопрос уже не про SRP. А по поводу кол-ва объектов — повторюсь — SRP не про то что нужно все измельчать, а про то, как делить.
Прежде чем «грязнить» код — есть много других возможностей:
1) Большинство проблем происходят из O-сложности. Если O — побеждена — переходим на уровень малой крови.
2) Делаем синглтоны или кэши для часто используемых объектов, меняем ref на value типы (если позволяет инфраструктура) — итд.
3) Если и это не помогло — то часто можно упростить или реорганизовать архитектуру во благо скорости и без ущерба смыслу (на этом этапе уже понятно как).
4) Для высоконагруженных — не забываем про горизонтальное масштабирование.
И только потом, когда все вышеперечисленное не помогло — начинаем крошить код во благо скорости. Но необходимость этого встречается редко. Как правило на мобилках и ARM. И, безусловно — тут все средства хороши.
Что самое главное — предыдущие 4 пункта сложно выполнить, если у вас там все SOLID-ы порушены вкривь и вкось.
Он говорит про изменение поведения, а не про то, что код нельзя трогать. Например — смена конекшн стринги из конфиг файла.
Или более сложный случай — вы хотите сменить одну SQL базу на другую — значит подготовьте бек-код так, что бы можно было менять провайдера DB, а не удалять старого и вместо него писать нового. Так же это говорит о том, что предпочитайте if-чикам — стратегии и композицию
Lsp утверждает, что наследование и интерфейсы должно быть обоснованно по смыслу, а не для экономии кода. Иными словами оно против структурной типизации в языках — где структурной типизации нет, так как это лютый самострел.
Есть ощущение, что вы принципиально пытаетесь переспорить изначальный тезис, что спортивно и прикольно.
«атомарные» — тестирующие минимальный объект. Я имею ввиду, что использование TDD в интеграционном ключе, тоесть тестирование больших кусков кода за раз, не тестируя маленькие — имеет тенденцию к «спецификации макарон в виде тестов». Кажется человечество еще не придумало как распутывать такие клубки.
Архитектурная «оптимизация» это хорошо, но речь про обычную, вызванную SRP. И это не заблуждение, по крайней мере если в стеке есть нормальные инструменты профайлинга. А вот экономия кол-ва типов — это кажется оскорбление чуств верующих в прекрасное, да и будущих разрабов за компанию.
Ocp не запрещает и не поощряет рефакторинг. Он говорит что «изменение поведения кода нужно делать через… поведения-)». Иными словами — не надо хардкодить.
Либо я вас по прежнему не понял, либо вы путаете LSP и проблемы ковариантных/контравариантных преобразований и структурной типизации.
SRP. Проблема в понимании SRP как «дроби пока дробится». Но это не так. SRP — он же принцип единой изменчивости, единой ответственности и локализации фичей — говорит про границы декомпозиции, а не про ее размер. Нарушение Srp — это не только God-object, но и размазывание и дублирование по коду какой-то конкретной фичи. Дублирование кода, как вы понимаете отнють не ускоряет даже MVP. Ежели мы касаемся вопросов оптимизации — то «преждевременная оптимизация — зло (с)». Кстати неверная декомпозиция, приводит к костылям в коде и кровавому легаси.
OCP, при нормальном проектировании необходим для выполнения SRP и тестирования. А устаревшие интерфейсы можно просто удалить. Другое дело что вы не сможете их удалить если у вас нарушен OCP, что приведет вас к кровавому легаси.
LSP говорит что наследование и полиморфизм должны быть сделаны с умом, в том числе. Везде где есть нарушение LSP — начинается задница и борьба с иерархиями. Как правило тут начинают говорить про кровавое легаси.
ISP не приводит к интерфейсам из одного метода. Аналогично SRP, ISP говорит о том как нужно делать интерфейсы в спорной ситуации, и позволяет развязать жуткие клубки зависимостей. Это крайне необходимо для больших команд, сложной бизнесс логики и ограниченных контекстов. В противном случае — ну вы поняли ;)
DIP — соглашусь. Есть доказательства того, что слепое следование DIP приводит к рекурсивному генерированию кода. Таким образом DIP это не принцип, а ползунок, пренебрегать которым не стоит ни в одну, ни в другую сторону.
С другой стороны Visitor это не только паттерн матчинг но и SRP точка. Как раз та самая логическая группировка процедур обработки узлов.
И в довесок к ништякам — Visitor есть очень удобный интерфейс для использования в DFS. Тоесть отлично разделяются алгоритм обхода дерева и алгоритм преобразования.
Также есть вопрос к процедурному обходу — по читабельности:
В «безвизиторном» варианте обхода дерева с огромной вариативностью алгоритмов, вопрос группировки всех этих процедур встает очень остро, и, по сути, решается внимательностью и совестью разработчика. Это ок — если разраб один. Это очень не ок когда разрабов несколько и у каждого немного свое мнение, а среди них еще и затаился человек-снежинка.
Итого. В данном случае, визитор это:
— SRP
— Читабельность
— Строгая типизация
То есть больше, чем просто паттерн матчинг фиксированной иерархии в одном модуле.
С проблемой каши из кода, при подходе DFS по синтаксическим нодам со свитчами и процедурами я столкнулся уже при N = 7, и M = 30.
Также, у каждого типа синтаксической ноды есть множество принципиально различных атрибутов, нужных для анализа и принятия решений, что есть жирный, статически типизированный плюс (привет Roslyn).
Один такой визитор будет инкапсулировать один из N алгоритмов для всех M нод. В случае дубляжа кода можно воспользоваться как полиморфизмом так и наследованием.
Стабильность нет кора — не ниже джава стека.
Стабильность мини и микросервисной архитектуры — не зависит от того что сервисы на разных платформах
Стоимость разрабов джава — неоправданно выше.
Стоимость разработки на джава — немного выше
Скорость работы стека кора — выше
Вопрос: при выборе какой технологии для нового сервиса (Java или Net) у вас появятся основания для увольнения?
То, что их недавно выложили де Юро — не сделало WCF стандартом де факто, сколь угодно интересным комьюнити или другим компаниям.
Вероятно я вам не понял — вы имеете ввиду middleware asp.net core?