Всем привет!
Продолжаю читать книгу "Эффективная работа с унаследованным кодом". Наткнулся на интересную мысль, на первый взгляд подтверждающую тезис: разработка - искусство компромиссов.
Мы все используем библиотеки. Когда нужно написать тест (а книга в основном про то, как упростить написание тестов для legacy) - часто в наш класс передаются библиотечные объекты. И тут могут быть две проблемы:
1) объект-синглтон
2) final объект или объект с методами, которые не возможно переопределить.
Эти две проблемы приводят к одному и тому же - мы не можем создать mock, приходится инициализировать для тестов реальный объект из библиотеки. А он может быть "тяжелым", превращающий модульный тест в интеграционный.
Как вариант решения этой проблемы предлагается:
1) для singleton создавать фасад, который разрешает замену объекта через setter, т.е. по сути нарушать суть паттерна singleton
2) для final методов и объектов - убрать final, объявляя их логическими final в документации.
Оба предложения по сути об одном - нарушаем принципы проектирования на уровне языка, зато делаем возможным тестирование кода.
В целом - "соль" в этом есть. Код без тестов опаснее кода, нарушающего принципы проектирования. Но я бы уточнил, что оба метода - это крайние меры. А по хорошему, если вы разрабатываете код, который кто-то когда-то будет тестировать - надо заранее озаботится о тестируемости, и именно:
1) не создавать singleton самому, использовать для этого IoC контейнер. Spring если библиотека внутренняя, и у вас используется Spring, или CDI аннотации в других случаях. Причем это актуально не только для разработчиков библиотек, а для всех.
2) создавать интерфейсы для классов, вынесенных в клиентское API. Есть интерфейс - всегда можно создать mock в тесте без всяких ухищрений. Я против создания интерфейсов всегда и везде https://t.me/javaKotlinDevOps/235, но Java API - это как раз подходящий случай. Одних интерфейсов конечно же мало, нужно еще и четкое разделение на модель и сервисы. Первые можно использоваться AS IS в тестах, вторые часто приходится мокать. Да, спроектировать все правильно не так-то легко, но как говорится - дорогу осилит идущий.
#book_review #ood #dev_compromises #libraries
Продолжаю читать книгу "Эффективная работа с унаследованным кодом". Наткнулся на интересную мысль, на первый взгляд подтверждающую тезис: разработка - искусство компромиссов.
Мы все используем библиотеки. Когда нужно написать тест (а книга в основном про то, как упростить написание тестов для legacy) - часто в наш класс передаются библиотечные объекты. И тут могут быть две проблемы:
1) объект-синглтон
2) final объект или объект с методами, которые не возможно переопределить.
Эти две проблемы приводят к одному и тому же - мы не можем создать mock, приходится инициализировать для тестов реальный объект из библиотеки. А он может быть "тяжелым", превращающий модульный тест в интеграционный.
Как вариант решения этой проблемы предлагается:
1) для singleton создавать фасад, который разрешает замену объекта через setter, т.е. по сути нарушать суть паттерна singleton
2) для final методов и объектов - убрать final, объявляя их логическими final в документации.
Оба предложения по сути об одном - нарушаем принципы проектирования на уровне языка, зато делаем возможным тестирование кода.
В целом - "соль" в этом есть. Код без тестов опаснее кода, нарушающего принципы проектирования. Но я бы уточнил, что оба метода - это крайние меры. А по хорошему, если вы разрабатываете код, который кто-то когда-то будет тестировать - надо заранее озаботится о тестируемости, и именно:
1) не создавать singleton самому, использовать для этого IoC контейнер. Spring если библиотека внутренняя, и у вас используется Spring, или CDI аннотации в других случаях. Причем это актуально не только для разработчиков библиотек, а для всех.
2) создавать интерфейсы для классов, вынесенных в клиентское API. Есть интерфейс - всегда можно создать mock в тесте без всяких ухищрений. Я против создания интерфейсов всегда и везде https://t.me/javaKotlinDevOps/235, но Java API - это как раз подходящий случай. Одних интерфейсов конечно же мало, нужно еще и четкое разделение на модель и сервисы. Первые можно использоваться AS IS в тестах, вторые часто приходится мокать. Да, спроектировать все правильно не так-то легко, но как говорится - дорогу осилит идущий.
#book_review #ood #dev_compromises #libraries
Telegram
(java || kotlin) && devOps
Всем привет!
В коде я часто вижу раздражающий меня паттерн - интерфейс с одной реализацией.
Почему так делается и когда так делать не стоит?
Для начала - когда полезны интерфейсы:
1) соблюдение принципа инверсии зависимостей, буква D из SOLID. См. ht…
В коде я часто вижу раздражающий меня паттерн - интерфейс с одной реализацией.
Почему так делается и когда так делать не стоит?
Для начала - когда полезны интерфейсы:
1) соблюдение принципа инверсии зависимостей, буква D из SOLID. См. ht…
👍3
Я вот прямо зауважал Русскую редакцию Microsoft после прочтения этой фразы в Совершенном коде Макконела.
Жаль имя переводчика не указали)
#book_review
Жаль имя переводчика не указали)
#book_review
😁7🔥1
Продолжение по Совершенному коду Макконела.
Надо сказать, что хорош конечно же не только переводчик, но в первую очередь сам автор.
Цитата:
P.S. заметки по книге еще будут)
#book_review
Надо сказать, что хорош конечно же не только переводчик, но в первую очередь сам автор.
Цитата:
Убедитесь, что всем известна цена изменения требований
Думая о новой функции, клиенты приходят в возбуждение.
Кровь у них разжижается, переполняет продолговатый мозг, и они впадают в эйфорию, забывая обо всех собраниях, посвященных обсуждению требований... и всех документах.
Угомонить таких одурманенных новыми функциями людей проще всего заявив:
«Ого, это действительно прекрасная идея!
Но ее нет в документе требований, поэтому я должен пересмотреть график работы и смету, чтобы вы могли решить, хотите ли вы реализовать это прямо сейчас или позднее».
Слова «график» и «смета» отрезвляют куда лучше, чем кофе и холодный душ, и многие требования быстро превращаются в пожелания.
P.S. заметки по книге еще будут)
#book_review
😁6
AI и Совершенный код - видишь связь?)
А она есть)
Одна из важных вещей, которую "с порога" подсвечивает Макконнелл (да, оказывается двойных согласных там целых три) - собственно программирование делится на несколько этапов: детальное проектирование, кодирование и отладка, модульные и интеграционные тесты, интеграция на стенде.
Возможно даже отладку стоило бы выделить отдельно, т.к. часто по факту так и происходит. Но не суть.
Важно, что это все такие же полноправные этапы большого процесса разработки, как и сбор требований, проработка архитектуры, тестирование, сопровождение...
Особо интересно проектирование. Объем его может варьироваться - от размера проекта или итерации при Agile разработке, от детальности проработки архитектуры, но такой этап есть.
И важно о нем не забывать.
Так вот, если применять AI в разработке - забыть о проектировании становится сложнее.
Если сразу попросить агента сделать что-то - результат будет каким-то)
Даже простейшая задачка типа: "обнови Gradle wrapper" - может привести к непредсказуемому результату.
Например, агент попытается вытянуть Gradle с родного сайта, хотя это может быть запрещено политикой компании (ака кровавый enterprise).
Или обновить до такой версии, которая потребует обновления Java.
Да и заодно и обновить код под новую версию Java)
Не даром некоторые AI агенты в IDE имеют два режима - Plan и Act.
Второй важный момент - как раз тут вырисовывается водораздел: проектирование - ведет человек, возможно совместно с AI. Кодирование и отладку вполне можно оставить AI.
И последнее - возможно AI не ускорит разработку, а сделает ее более качественной, за счет большего упора на проектирование и снятие рутины с разработчика.
#ai #book_review
А она есть)
Одна из важных вещей, которую "с порога" подсвечивает Макконнелл (да, оказывается двойных согласных там целых три) - собственно программирование делится на несколько этапов: детальное проектирование, кодирование и отладка, модульные и интеграционные тесты, интеграция на стенде.
Возможно даже отладку стоило бы выделить отдельно, т.к. часто по факту так и происходит. Но не суть.
Важно, что это все такие же полноправные этапы большого процесса разработки, как и сбор требований, проработка архитектуры, тестирование, сопровождение...
Особо интересно проектирование. Объем его может варьироваться - от размера проекта или итерации при Agile разработке, от детальности проработки архитектуры, но такой этап есть.
И важно о нем не забывать.
Так вот, если применять AI в разработке - забыть о проектировании становится сложнее.
Если сразу попросить агента сделать что-то - результат будет каким-то)
Даже простейшая задачка типа: "обнови Gradle wrapper" - может привести к непредсказуемому результату.
Например, агент попытается вытянуть Gradle с родного сайта, хотя это может быть запрещено политикой компании (ака кровавый enterprise).
Или обновить до такой версии, которая потребует обновления Java.
Да и заодно и обновить код под новую версию Java)
Не даром некоторые AI агенты в IDE имеют два режима - Plan и Act.
Второй важный момент - как раз тут вырисовывается водораздел: проектирование - ведет человек, возможно совместно с AI. Кодирование и отладку вполне можно оставить AI.
И последнее - возможно AI не ускорит разработку, а сделает ее более качественной, за счет большего упора на проектирование и снятие рутины с разработчика.
#ai #book_review
👍2
И снова к Совершенному коду)
Там есть раздел про выбор языка.
Интересный аспект: язык влияет на мышление, и, следовательно, на процесс проектирования. Точнее на его уровень - более высокоуровневое или низкоуровневое. Нужно ли думать про удаление мусора, закрытие ресурсов, безопасностью работы с памятью ...
И в итоге язык влияет на скорость разработки, причем несколькими путями:
1) на незнакомом языке происходит замедление разработки до 30%
2) в разных языках объем полезной работы, которую в среднем производит одна строка кода, отличается. Это не только и не столько про скорость написания кода, но и про скорость чтения и понимания. Данный показатель как раз иллюстрирует табличка из поста.
Из таблицы видно, что Java никогда не была лидером по выразительности. Но надо отметить - в последние лет 10 работа над этим ведется.
Kotlin я думаю даже выше 6-ки будет.
#lang #book_review
Там есть раздел про выбор языка.
Интересный аспект: язык влияет на мышление, и, следовательно, на процесс проектирования. Точнее на его уровень - более высокоуровневое или низкоуровневое. Нужно ли думать про удаление мусора, закрытие ресурсов, безопасностью работы с памятью ...
И в итоге язык влияет на скорость разработки, причем несколькими путями:
1) на незнакомом языке происходит замедление разработки до 30%
2) в разных языках объем полезной работы, которую в среднем производит одна строка кода, отличается. Это не только и не столько про скорость написания кода, но и про скорость чтения и понимания. Данный показатель как раз иллюстрирует табличка из поста.
Из таблицы видно, что Java никогда не была лидером по выразительности. Но надо отметить - в последние лет 10 работа над этим ведется.
Kotlin я думаю даже выше 6-ки будет.
#lang #book_review
И снова минутка цитат, подтверждающая мысль, что Стив (Макконнелл) умеет жечь глаголом сердца людей:
P.S. Справедливости ради отдельно обсуждается кейс mixin - классов, не являющихся самостоятельной сущностью, а лишь содержащих какой-то общеупотребительный функционал. Пример: Comparator. То, что в Java реализуется как интерфейс с методами по умолчанию. Этот вариант - ок, но здесь множественное наследование лишь средство подмешать данный функционал в класс
#book_review
Наследование — мощный и... довольно опасный инструмент. В некотором смысле наследование похоже на цепную пилу: при соблюдении мер предосторожности оно может быть невероятно полезным, но при неумелом обращении последствия могут оказаться очень и очень серьезными.
Если наследование — цепная пила, то множественное наследование — это старинная цепная пила с барахлящим мотором, не имеющая предохранителей и не поддерживающая автоматического отключения. Иногда такой инструмент может пригодиться, но большую часть времени его лучше хранить в гараже под замком.
P.S. Справедливости ради отдельно обсуждается кейс mixin - классов, не являющихся самостоятельной сущностью, а лишь содержащих какой-то общеупотребительный функционал. Пример: Comparator. То, что в Java реализуется как интерфейс с методами по умолчанию. Этот вариант - ок, но здесь множественное наследование лишь средство подмешать данный функционал в класс
#book_review
Небольшое дополнение по принципу Liskov к https://t.me/javaKotlinDevOps/182
Краткое содержание предыдущей серии - основной способ нарушить это принцип в Java - сломать семантический контракт метода.
А еще нарушение этого принципа может подсветить плохую абстракцию, выбранную для класса.
Пример: кто-то решил сделать один базовый класс для вклада и кредита. В этом случае метод monthlyPayment() покажет, что это была плохая идея. Т.к. в вызывающем классе нам в зависимости от конкретного потомка - вклада или кредита - придется брать знак минус или плюс для вычислений. Что нарушает принцип Liskov.
P.S. Это тоже Совершенный код)
#solid #book_review
Краткое содержание предыдущей серии - основной способ нарушить это принцип в Java - сломать семантический контракт метода.
А еще нарушение этого принципа может подсветить плохую абстракцию, выбранную для класса.
Пример: кто-то решил сделать один базовый класс для вклада и кредита. В этом случае метод monthlyPayment() покажет, что это была плохая идея. Т.к. в вызывающем классе нам в зависимости от конкретного потомка - вклада или кредита - придется брать знак минус или плюс для вычислений. Что нарушает принцип Liskov.
P.S. Это тоже Совершенный код)
#solid #book_review
Telegram
(java || kotlin) && devOps
Всем привет!
Продолжая тему SOLID. Есть в этой аббревиатуре буква L - принцип Барбары Лисков, который гласит, что все потомки должны соблюдать контракт, объявленный в родителе.
Объясню на пальцах. Есть класс или даже скорее интерфейс А. Есть класс Б, как…
Продолжая тему SOLID. Есть в этой аббревиатуре буква L - принцип Барбары Лисков, который гласит, что все потомки должны соблюдать контракт, объявленный в родителе.
Объясню на пальцах. Есть класс или даже скорее интерфейс А. Есть класс Б, как…
Нужно ли читать чужой код?
Я не про код-ревью, а про процесс разработки.
С одной стороны я всегда топлю за то, что если есть проблемы с внешней библиотекой - не понятно, почему метод возвращает такой результат или ошибку - залезь в ее код, и посмотри.
Но Стив Макконнелл (да, снова Совершенный код) подсвечивает один интересный момент - разницу между синтаксической и семантической инкапсуляцией внутренностей класса.
С синтаксической все просто - объявил по максимуму все private и профит.
А вот семантическая, а точнее ее нарушение - это наши догадки насчет внутреннего устройства чужого класса, влияющие на наш код.
А догадки эти обычно возникают после чтения кода...
Тут еще хорошая аналогия с работой разработчика и тестировщика. Для тестировщика код - это черный ящик. А для разработчика - нет. Или все же да?)
Я для себя решил так. Если найдено неочевидное\неправильное поведение внешней библиотеки, ее автор доступен и готов к сотрудничеству - стоит по советам Стива зайти к нему и попросить поправить.
А если хотя бы одно из этих условий не выполняется - можно и нужно залезть в код. И возможно даже завязаться на его внутренние детали реализации. Да, да, я про костыль.
Но с обязательными условиями:
1) proxy слой
2) с подробным комментарием что делается и почему.
И с заведением техдолга если все же есть возможность поправить эту проблему прямым образом.
#book_review #code
Я не про код-ревью, а про процесс разработки.
С одной стороны я всегда топлю за то, что если есть проблемы с внешней библиотекой - не понятно, почему метод возвращает такой результат или ошибку - залезь в ее код, и посмотри.
Но Стив Макконнелл (да, снова Совершенный код) подсвечивает один интересный момент - разницу между синтаксической и семантической инкапсуляцией внутренностей класса.
С синтаксической все просто - объявил по максимуму все private и профит.
А вот семантическая, а точнее ее нарушение - это наши догадки насчет внутреннего устройства чужого класса, влияющие на наш код.
А догадки эти обычно возникают после чтения кода...
Тут еще хорошая аналогия с работой разработчика и тестировщика. Для тестировщика код - это черный ящик. А для разработчика - нет. Или все же да?)
Я для себя решил так. Если найдено неочевидное\неправильное поведение внешней библиотеки, ее автор доступен и готов к сотрудничеству - стоит по советам Стива зайти к нему и попросить поправить.
А если хотя бы одно из этих условий не выполняется - можно и нужно залезть в код. И возможно даже завязаться на его внутренние детали реализации. Да, да, я про костыль.
Но с обязательными условиями:
1) proxy слой
2) с подробным комментарием что делается и почему.
И с заведением техдолга если все же есть возможность поправить эту проблему прямым образом.
#book_review #code
👍1💯1
assert в боевом коде?
На всякий случай напомню https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html
Пример:
Важной особенностью assert-ов является тот факт, что по умолчанию проверки, выполняемые в assert, выключены.
Для включения нужно передать опцию -ea (-enableassertions) при запуске, просто или с указанием пакета.
И как раз этот факт является одним из двух гвоздей, убивающих данную фичу.
Второй гвоздь - позиционирование assert-ов примерно такое же, как и у отладочных логов.
Это я читаю как в Совершенном коде (да, он спонсор данного поста))), так и в статье выше обсуждается как бы их удалить из кода.
Теперь на отойдем на шаг назад.
Все ошибки во входящих данных можно разделить на две категории - ожидаемые и нет.
Это деление не дискретное, а непрерывное, но в любом случае приводит к вопросу - как проверять данные на неожиданные ошибки? Нужно ли это делать вообще?
С ожидаемыми все понятно - пишем проверку, if и далее возврат кода ошибки\exception\запись в лог.
А неожиданные - что с ними делать?
Самый яркий пример - проблема nullability в Java. Объекты у нас везде, тогда в теории почти в любом методе нужна проверка на null.
Или не нужна, т.к. это сильно увеличивает объем кода и ухудшает читаемость.
Тут как раз предлагается использовать assert - для ошибок, которых при нормальной работе программы быть не должно.
Почему мне это не нравится.
Как раз по двум причинам выше - мы решили проверить данные и вроде закрылись assert-ом.
А потом кто-то убрал -ea в ПРОМе. И вообще говоря такой путь часто рекомендуют - разработка с assert, ПРОМ - без.
Или кто-то удалил assert посчитав его отладочным. Проект то живет долго, люди меняются.
Решением проблемы является созданием слоя валидации (или расположение валидации на сервисном слое). До того, как данные прошли этот слой считаем, что там может быть что угодно.
После - минимум проверок.
Да, часть валидации происходит в API (маппинг API в объекты), но не вся и не всегда только API является поставщиком данных.
Еще предлагаемый кейс для assert - pre-condition, post-condition, инварианты - т.е. реализация контрактов для методов и классов в коде.
Причем часто основная идея здесь не проверка условий, а именно документирование контракта с помощью assert.
И тут снова вступает в силу ненулевая вероятность удаления assert-ов, т.к. в боевом коде они выглядят чужеродно.
Я тут за JavaDoc, т.к. по JavaDoc можно построить отдельный jar с документацией, который может использовать IDE.
С контрактами есть отдельный важный момент: контракт - это часть класса, выносить его в слой валидации плохо.
Но в таком случае лучше его реализовать или обычным кодом или JavaDoc.
Итого остается редкий кейс разработки кусочка монолита, когда нет уверенности, что контракты соблюдаются. Условно разработка чего-то уровня JDK.
Используете ли assert в боевом коде? Для чего?
#book_review #contracts #validations
На всякий случай напомню https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html
Пример:
Connection conn = getConnection();
assert conn != null;
Важной особенностью assert-ов является тот факт, что по умолчанию проверки, выполняемые в assert, выключены.
Для включения нужно передать опцию -ea (-enableassertions) при запуске, просто или с указанием пакета.
И как раз этот факт является одним из двух гвоздей, убивающих данную фичу.
Второй гвоздь - позиционирование assert-ов примерно такое же, как и у отладочных логов.
Это я читаю как в Совершенном коде (да, он спонсор данного поста))), так и в статье выше обсуждается как бы их удалить из кода.
Теперь на отойдем на шаг назад.
Все ошибки во входящих данных можно разделить на две категории - ожидаемые и нет.
Это деление не дискретное, а непрерывное, но в любом случае приводит к вопросу - как проверять данные на неожиданные ошибки? Нужно ли это делать вообще?
С ожидаемыми все понятно - пишем проверку, if и далее возврат кода ошибки\exception\запись в лог.
А неожиданные - что с ними делать?
Самый яркий пример - проблема nullability в Java. Объекты у нас везде, тогда в теории почти в любом методе нужна проверка на null.
Или не нужна, т.к. это сильно увеличивает объем кода и ухудшает читаемость.
Тут как раз предлагается использовать assert - для ошибок, которых при нормальной работе программы быть не должно.
Почему мне это не нравится.
Как раз по двум причинам выше - мы решили проверить данные и вроде закрылись assert-ом.
А потом кто-то убрал -ea в ПРОМе. И вообще говоря такой путь часто рекомендуют - разработка с assert, ПРОМ - без.
Или кто-то удалил assert посчитав его отладочным. Проект то живет долго, люди меняются.
Решением проблемы является созданием слоя валидации (или расположение валидации на сервисном слое). До того, как данные прошли этот слой считаем, что там может быть что угодно.
После - минимум проверок.
Да, часть валидации происходит в API (маппинг API в объекты), но не вся и не всегда только API является поставщиком данных.
Еще предлагаемый кейс для assert - pre-condition, post-condition, инварианты - т.е. реализация контрактов для методов и классов в коде.
Причем часто основная идея здесь не проверка условий, а именно документирование контракта с помощью assert.
И тут снова вступает в силу ненулевая вероятность удаления assert-ов, т.к. в боевом коде они выглядят чужеродно.
Я тут за JavaDoc, т.к. по JavaDoc можно построить отдельный jar с документацией, который может использовать IDE.
С контрактами есть отдельный важный момент: контракт - это часть класса, выносить его в слой валидации плохо.
Но в таком случае лучше его реализовать или обычным кодом или JavaDoc.
Итого остается редкий кейс разработки кусочка монолита, когда нет уверенности, что контракты соблюдаются. Условно разработка чего-то уровня JDK.
Используете ли assert в боевом коде? Для чего?
#book_review #contracts #validations
"Цените легкость чтения кода выше, чем удобство его написания"
Я постоянно топлю за то, что код должен легко читаться.
И цитата из сами понимаете какой книжки как нельзя лучше иллюстрирует данный принцип.
Т.к. если не задумываться об удобстве чтения кода, то мы получаем:
1) дублирование - проще же скопировать, чем выделить новую абстракцию
2) вечный техдолг из-за не проведенного вовремя рефакторинга
3) божественные классы: пусть новый метод Y не вписывается в абстракцию класса Х, но чтобы это заметить - надо выйти за рамки текущей задачи
4) огромные иерархии классов, длинные методы - проще добавить код по аналогии
5) слабое покрытие тестами - код работает, мне он на данный момент абсолютно понятен. Зачем тесты писать - ради тестов?
6) методы и классы с неактуальными названиями
7) все методы в классе являются public - они же вызываются из public метода, да и вдруг кому-то пригодятся
8) спагетти-код
...
и получаем легаси в итоге.
#book_review
Я постоянно топлю за то, что код должен легко читаться.
И цитата из сами понимаете какой книжки как нельзя лучше иллюстрирует данный принцип.
Т.к. если не задумываться об удобстве чтения кода, то мы получаем:
1) дублирование - проще же скопировать, чем выделить новую абстракцию
2) вечный техдолг из-за не проведенного вовремя рефакторинга
3) божественные классы: пусть новый метод Y не вписывается в абстракцию класса Х, но чтобы это заметить - надо выйти за рамки текущей задачи
4) огромные иерархии классов, длинные методы - проще добавить код по аналогии
5) слабое покрытие тестами - код работает, мне он на данный момент абсолютно понятен. Зачем тесты писать - ради тестов?
6) методы и классы с неактуальными названиями
7) все методы в классе являются public - они же вызываются из public метода, да и вдруг кому-то пригодятся
8) спагетти-код
...
и получаем легаси в итоге.
#book_review
Рубрика "Загадки от Стива".
Если объект класса создается в коде всего один раз - стоит заменить такой класс объектом.
О чем речь?
Пример:
у нас есть разные виды карт: Мир, Мир Supreme, Visa Classic, Visa Digital..
Можно завести под каждый тип отдельный подкласс, но в большинстве случае достаточно параметризовать все отличия разных типов карт и просто создавать объекты одного класса.
#book_review #oop
Если объект класса создается в коде всего один раз - стоит заменить такой класс объектом.
О чем речь?
у нас есть разные виды карт: Мир, Мир Supreme, Visa Classic, Visa Digital..
Можно завести под каждый тип отдельный подкласс, но в большинстве случае достаточно параметризовать все отличия разных типов карт и просто создавать объекты одного класса.
#book_review #oop
Еще одна загадка "от Стива":
Цитата:
"В одном исследовании 450 методов было обнаружено, что дефекты отсутствовали у 50% методов, обладающих высокой связностью,
и только в 18% методов с низкой связностью (Card, Church, and Agresti, 1986).
Другое исследование методов (это просто совпадение, хотя и весьма необычное) показало,
что в сравнении с методами, имеющими самое низкое отношение «сопряжение/связность» (coupling-to-cohesion),
методы с максимальным отношением «сопряжение/связность» содержали в 7 раз больше ошибок,
а исправление этих методов было в 20 раз более дорогим (Selby and Basili, 1991)."
Речь про то, что в методах с высокой связностью кода - single responsibility по сути - меньше ошибок.
Почему?
Мой ответ:
низкая связность может иметь одну из двух причин:
1) плохое проектирование = больше ошибок
2) плохо проведенный\не проведенный вовремя рефакторинг, когда в метод добавили новый функционал, "гвоздями прибив" его к существующему без нормального тестирования
Ну и банально - больше функционала в методе = больше метод = больше вероятность ошибки. Но тут зависимость не линейная, при нормальном проектировании.
#book_review #quest
Цитата:
"В одном исследовании 450 методов было обнаружено, что дефекты отсутствовали у 50% методов, обладающих высокой связностью,
и только в 18% методов с низкой связностью (Card, Church, and Agresti, 1986).
Другое исследование методов (это просто совпадение, хотя и весьма необычное) показало,
что в сравнении с методами, имеющими самое низкое отношение «сопряжение/связность» (coupling-to-cohesion),
методы с максимальным отношением «сопряжение/связность» содержали в 7 раз больше ошибок,
а исправление этих методов было в 20 раз более дорогим (Selby and Basili, 1991)."
Речь про то, что в методах с высокой связностью кода - single responsibility по сути - меньше ошибок.
Почему?
низкая связность может иметь одну из двух причин:
1) плохое проектирование = больше ошибок
2) плохо проведенный\не проведенный вовремя рефакторинг, когда в метод добавили новый функционал, "гвоздями прибив" его к существующему без нормального тестирования
Ну и банально - больше функционала в методе = больше метод = больше вероятность ошибки. Но тут зависимость не линейная, при нормальном проектировании.
#book_review #quest
🔥1
И еще один вопрос от Стива, попроще:
"Используйте утвердительные имена булевых переменных"
Почему?
Ответ:
чтобы избежать вот такого кода:
if ((!notFound) && ... )
затрудняющего чтение.
А кстати в Python все еще хуже:
if (not notFound) and ...:
#book_review
"Используйте утвердительные имена булевых переменных"
Почему?
чтобы избежать вот такого кода:
if ((!notFound) && ... )
затрудняющего чтение.
А кстати в Python все еще хуже:
if (not notFound) and ...:
#book_review
Что такое совершенный код?
Читая одноименную книгу (да, я снова о ней), у меня возникла уверенность в том, что автор практикующий и очень педантичный, уделяющий внимание деталям разработчик.
В принципе, для этого книгу можно не читать, достаточно оценить ее размер)
Но тем не менее.
По каждой казалось бы простой теме: переменные, методы, классы, условные операторы, циклы... - есть есть что сказать, и сказать много.
И в большинстве случаев - по делу.
Пример.
Я всегда топлю за читаемость кода.
Но никогда не задумывался о расположении локальных переменных.
А Стив задумывался)
Он вводит понятия частоты использования и времени жизни переменной.
Вот это неправильно, т.к.
1) не нужно разделять объявление, инициализацию и использование переменной, т.е. переменную для цикла надо объявлять прямо перед ним.
2) блоки по поиску файла и заметки нужно четко разделить, чтобы было легко рефакторить и при необходимость разделить на методы.
3) еще один риск - если между объявлением переменной и ее использованием много кода - возрастает вероятность "случайно" ее поменять так, что сломается использующий переменную код.
Т.е. частота использования (число строк с переменной/число строк от первого до последнего появления переменной) должна быть высокой, время жизни - коротким.
И это увеличивает читаемость. Вроде простая штука, но отдельно о ней не задумываешь.
#book_review
Читая одноименную книгу (да, я снова о ней), у меня возникла уверенность в том, что автор практикующий и очень педантичный, уделяющий внимание деталям разработчик.
В принципе, для этого книгу можно не читать, достаточно оценить ее размер)
Но тем не менее.
По каждой казалось бы простой теме: переменные, методы, классы, условные операторы, циклы... - есть есть что сказать, и сказать много.
И в большинстве случаев - по делу.
Пример.
Я всегда топлю за читаемость кода.
Но никогда не задумывался о расположении локальных переменных.
А Стив задумывался)
Он вводит понятия частоты использования и времени жизни переменной.
var fileFound = false;
var noteFound = false;
...
while (!found) {
...
}
...
noteFound = findNotes(...);
Вот это неправильно, т.к.
1) не нужно разделять объявление, инициализацию и использование переменной, т.е. переменную для цикла надо объявлять прямо перед ним.
2) блоки по поиску файла и заметки нужно четко разделить, чтобы было легко рефакторить и при необходимость разделить на методы.
3) еще один риск - если между объявлением переменной и ее использованием много кода - возрастает вероятность "случайно" ее поменять так, что сломается использующий переменную код.
Т.е. частота использования (число строк с переменной/число строк от первого до последнего появления переменной) должна быть высокой, время жизни - коротким.
И это увеличивает читаемость. Вроде простая штука, но отдельно о ней не задумываешь.
#book_review
И чтобы "зафиналить" тему с множественными отрицаниями в условиях пару цитат из книжки)))
P.S. Кто-нибудь пробовал вычислить вторую фразу?)
#book_review
Я не не нетупой.
Гомер Симпсон
(Homer Simpson)
Не немногие люди не имеют проблем с непониманием некоротких неположительных фраз, т. е. большинство людей имеют трудности с пониманием большого количества отрицаний.
P.S. Кто-нибудь пробовал вычислить вторую фразу?)
#book_review
🔥1
Два подхода к написанию функций (методов).
Первый - у метода должна быть одна точка выхода (в общем случае - как можно меньше точек выхода). А точка входа по умолчанию одна. Потому что это улучшает читаемость.
Второй - если у метода есть предусловия, то их нужно проверять с помощью охранных выражений в начале метода. Т.к. это уменьшает вложенность и опять же улучшает читаемость)
Т.е
vs
Второй вариант может быть длиннее первого, если всегда "как завещано" использовать фигурные скобки. Но в данном случае видится, что можно их убрать для улучшения читаемости.
И тогда мы получаем более понятный из-за линейной структуры.
В Совершенном коде автор тоже за второй вариант. Присоединяюсь и считаю, что второй вариант - это база.
Возможно есть кейсы, когда он не применим, но я пока придумать не могу.
Даже на 2 условиях. Даже если недостаточно простого return.
#book_review #code
Первый - у метода должна быть одна точка выхода (в общем случае - как можно меньше точек выхода). А точка входа по умолчанию одна. Потому что это улучшает читаемость.
Второй - если у метода есть предусловия, то их нужно проверять с помощью охранных выражений в начале метода. Т.к. это уменьшает вложенность и опять же улучшает читаемость)
Т.е
public void processUser(User user) {
if (user != null) {
if (user.getAge() >= 18) {
if (validateEmail(user.getEmail()) != null && user.getEmail().contains("@")) {
if (user.isVerified()) {
performUserRegistration(user);
}
}
}
}
}vs
public void processUser(User user) {
if (user == null) return;
if (user.getAge() < 18) return;
if (user.getEmail() == null || !user.getEmail().contains("@")) return;
if (!user.isVerified()) return;
performUserRegistration(user);
}Второй вариант может быть длиннее первого, если всегда "как завещано" использовать фигурные скобки. Но в данном случае видится, что можно их убрать для улучшения читаемости.
И тогда мы получаем более понятный из-за линейной структуры.
В Совершенном коде автор тоже за второй вариант. Присоединяюсь и считаю, что второй вариант - это база.
Возможно есть кейсы, когда он не применим, но я пока придумать не могу.
Даже на 2 условиях. Даже если недостаточно простого return.
#book_review #code
Снова минутка цитат на канале:
Лично я вижу смысл в этом термине. И суть его в том, что в мире победившего ООП не стоит забывать об операциях (функциях) и о простоте.
Я уже писал про структурный дизайн - https://t.me/javaKotlinDevOps/400.
Напомню, там была речь о том, что не обязательно заводить кучу объектов и интерфейсов, если процесс простой и раскладывается на три части - подготовка данных, преобразования и запись.
Тогда его можно реализовать в виде сервиса, "чистой" модели и репозитория. К слову: ETL = Extract -> Transform -> Load - о том же.
Так вот - структурное программирование тоже раскладывает любой процесс на три части, только уже на уровне метода: последовательный код, условия и циклы.
Казалось бы - ну да, очевидно это так. Но есть нюанс - тут речь идет о проблеме множественных выходов из метода.
Структурное программирование появилось как ответ на широкое распространение оператора go to и призвано бороться с ним.
Опять вопрос - go to мы победили, тогда зачем эти "предания старины глубокой"?
Нет, не победили. Точнее победили, но не до конца.
Т.к. у нас есть множественный return и throw. А если копнуть глубже, и расширить проблему на любые прыжки по коду - break и continue.
Надо ли от них отказываться, учитывая что парадигма то вроде полезная?
На мой взгляд - нет.
Охранные выражения (guard pattern) - однозначно полезны.
throw для сигнализации об ошибке - тоже ок, они для этого создавались.
С рядом условий:
1) unchecked
2) не надо просто для передачи управления
3) должен быть определен слой\процесс перехвата исключений на уровне сервиса
break и continue - если без них код сложнее читать - тоже допустимы. Прыжки то небольшие, в рамках цикла. Хотя я ими пользуюсь редко, кейсов мало.
Т.е. снова приходим к искусству компромиссов.
Всякий раз, перед написанием такого оператора стоит подумать - нет ли других вариантов? Не усложняется ли он чтение и понимание кода?
Суть структурного программирования - код каждой операции должен просто читаться. Нет "спагетти-коду"!
#book_review #java #code #structure_programming #structure_xxx #principles
«Термин „структурное программирование“ был введён в 1969 году.
С тех пор термин „структурный“ применялся к любой деятельности в области ПО, включая структурный анализ, структурный дизайн и структурное валяние дурака».
Стив Макконнелл «Совершенный код».
Лично я вижу смысл в этом термине. И суть его в том, что в мире победившего ООП не стоит забывать об операциях (функциях) и о простоте.
Я уже писал про структурный дизайн - https://t.me/javaKotlinDevOps/400.
Напомню, там была речь о том, что не обязательно заводить кучу объектов и интерфейсов, если процесс простой и раскладывается на три части - подготовка данных, преобразования и запись.
Тогда его можно реализовать в виде сервиса, "чистой" модели и репозитория. К слову: ETL = Extract -> Transform -> Load - о том же.
Так вот - структурное программирование тоже раскладывает любой процесс на три части, только уже на уровне метода: последовательный код, условия и циклы.
Казалось бы - ну да, очевидно это так. Но есть нюанс - тут речь идет о проблеме множественных выходов из метода.
Структурное программирование появилось как ответ на широкое распространение оператора go to и призвано бороться с ним.
Опять вопрос - go to мы победили, тогда зачем эти "предания старины глубокой"?
Нет, не победили. Точнее победили, но не до конца.
Т.к. у нас есть множественный return и throw. А если копнуть глубже, и расширить проблему на любые прыжки по коду - break и continue.
Надо ли от них отказываться, учитывая что парадигма то вроде полезная?
На мой взгляд - нет.
Охранные выражения (guard pattern) - однозначно полезны.
throw для сигнализации об ошибке - тоже ок, они для этого создавались.
С рядом условий:
1) unchecked
2) не надо просто для передачи управления
3) должен быть определен слой\процесс перехвата исключений на уровне сервиса
break и continue - если без них код сложнее читать - тоже допустимы. Прыжки то небольшие, в рамках цикла. Хотя я ими пользуюсь редко, кейсов мало.
Т.е. снова приходим к искусству компромиссов.
Всякий раз, перед написанием такого оператора стоит подумать - нет ли других вариантов? Не усложняется ли он чтение и понимание кода?
Суть структурного программирования - код каждой операции должен просто читаться. Нет "спагетти-коду"!
#book_review #java #code #structure_programming #structure_xxx #principles
Telegram
(java || kotlin) && devOps
Всем привет!
Я уже писал про то, что не люблю код, в котором интерфейсы делаются ради интерфейсов. Самый яркий антипаттерн: интерфейс с единственной реализацией, лежащей рядом. Подозреваю, одной из причин такого проектирования является принцип Dependency…
Я уже писал про то, что не люблю код, в котором интерфейсы делаются ради интерфейсов. Самый яркий антипаттерн: интерфейс с единственной реализацией, лежащей рядом. Подозреваю, одной из причин такого проектирования является принцип Dependency…
Сложно не заметить, что последние пару месяцев у меня основная тема блока - "жемчужины" из книги Совершенный код.
Сегодня еще один пост, пост-"мысль вслух".
Самое удивительное в этой книжке - это два факта:
1) на английском она издана в 2004 году
2) она актуальна.
Я пытаюсь вспомнить, что же там устарело и вспоминаю лишь следующее:
а) в Java появились Enum (на тот момент еще нет)
б) Visual Basic умер (там есть примеры в числе прочего на VB)
в) IDE стали сильно лучше, считать открывающие и закрывающие скобки самому уже не надо
г) кажется все языки получили свой аналог xUnit, "костылить" свой подобный фреймворк тоже не надо
д) у нас есть git и история изменений в IDEA, "прихранивать" копию исходников перед изменением не нужно
е) на способы проверки повреждения данных в памяти из-за кривых указателей смотришь с ужасом, но все такие главы подписаны - для C\C++ и при желании их можно пропускать. Да и скорее всего большая часть этой информации актуальна до сих пор.
И пожалуй это все! За 20+ лет.
Даже обсуждение go to и глобальных переменных лишь на первый взгляд кажется не актуальным для Java...
Вот как надо книжки писать!
Вспоминается: "Новые песни сочиняет тот, у кого старые плохие". И это тот редкий случай, когда фраза справедлива)
#book_review #java
Сегодня еще один пост, пост-"мысль вслух".
Самое удивительное в этой книжке - это два факта:
1) на английском она издана в 2004 году
2) она актуальна.
Я пытаюсь вспомнить, что же там устарело и вспоминаю лишь следующее:
а) в Java появились Enum (на тот момент еще нет)
б) Visual Basic умер (там есть примеры в числе прочего на VB)
в) IDE стали сильно лучше, считать открывающие и закрывающие скобки самому уже не надо
г) кажется все языки получили свой аналог xUnit, "костылить" свой подобный фреймворк тоже не надо
д) у нас есть git и история изменений в IDEA, "прихранивать" копию исходников перед изменением не нужно
е) на способы проверки повреждения данных в памяти из-за кривых указателей смотришь с ужасом, но все такие главы подписаны - для C\C++ и при желании их можно пропускать. Да и скорее всего большая часть этой информации актуальна до сих пор.
И пожалуй это все! За 20+ лет.
Даже обсуждение go to и глобальных переменных лишь на первый взгляд кажется не актуальным для Java...
Вот как надо книжки писать!
Вспоминается: "Новые песни сочиняет тот, у кого старые плохие". И это тот редкий случай, когда фраза справедлива)
#book_review #java
💯5
Agile мир победил, микросервисы оказались сильней)
Вот данные из моей любимой книжки по разработке.
Медиану можно оценить ~9, среднее ~19, но среднее мало информативно из-за сильной правой асимметрии (маленьких команд сильно больше, чем больших).
Я бы из него сделал вывод, что микросервисы были всегда, просто раньше назывались по-другому — например, SOA (Service Oriented Architecture).
Но конечно ситуация меняется(хотел написать «не могла не измениться», но вспомнил свой же пост про двойное отрицание))) .
А вот более современные данные, не такие подробные.
Если сравнить 2003 и 2012 годы — медиана уменьшилась почти в 2 раза: 9 → 5.
Ещё есть данные опроса пользователей JetBrains (2024) https://www.jetbrains.com/lp/devecosystem-2024/
Медиана ≈ 6 человек, среднее ≈ 11. Примерно то же, что в 2012, только как ни странно числа стали чуть больше). Зато видно, что больших команд стало в 2+ раза меньше.
По моему опыту (кровавый enterprise) медианный размер команды на данный момент выше, ближе к 9. Но то, что 80% команд укладываются в 12 человек — сходится.
В 2003 году было ~60%.
Т.е. мы пока одной ногой шагнули в новую эру) enterprise требует жертв. Но тренд понятен.
О причинах — часть уже назвал: Agile и микросервисы. Ещё — улучшившийся инструментарий: IDE и фреймворки/библиотеки под любую потребность. А сейчас ещё и AI.
Так что думаю, что к 6 людям в команде мы все скоро придём. Будет ли меньше? В теории да: ВП, аналитик, 1–2 разработчика, тестировщик.
P.S. Т.к. в таблицах процент команд, а не процент людей - в командах более 12 человек все еще работает более 50% разработчиков. Было - более 80%. Интересно кто это?
P.P.S. Станет ли медианным значением 1 — ВП, который с помощью AI пилит и тестирует сервисы сам? ...Где-то да, уже сейчас. Но массово — может помешать Skynet. Или поспособствовать — сидишь себе в матрице, вайб-кодишь с AI микросервисы.
#book_review #agile #ai #microservices
Вот данные из моей любимой книжки по разработке.
Процентное соотношение проектов по размеру команды:
Размер команды | Доля
----------------|------
1–3 человека | 25%
4–10 человек | 30%
11–25 человек | 20%
26–50 человек | 15%
>50 человек | 10%
Источники: Beck and Perkins (1983), Highsmith (2002), Boehm and Turner (2003)
Медиану можно оценить ~9, среднее ~19, но среднее мало информативно из-за сильной правой асимметрии (маленьких команд сильно больше, чем больших).
Я бы из него сделал вывод, что микросервисы были всегда, просто раньше назывались по-другому — например, SOA (Service Oriented Architecture).
Но конечно ситуация меняется
А вот более современные данные, не такие подробные.
В исследовании Rodriguez et al. (JSS, 2012) на базе 951 проекта — 75% имели команду менее 10 человек. Медиана = 5, среднее ≈ 7.9.
Если сравнить 2003 и 2012 годы — медиана уменьшилась почти в 2 раза: 9 → 5.
Ещё есть данные опроса пользователей JetBrains (2024) https://www.jetbrains.com/lp/devecosystem-2024/
Размер команды | Доля
----------------|------
1 человек | 8%
2–7 человек | 49%
8–12 человек | 22%
13–20 человек | 10%
21–40 человек | 6%
>40 человек | 5%
Медиана ≈ 6 человек, среднее ≈ 11. Примерно то же, что в 2012, только как ни странно числа стали чуть больше). Зато видно, что больших команд стало в 2+ раза меньше.
По моему опыту (кровавый enterprise) медианный размер команды на данный момент выше, ближе к 9. Но то, что 80% команд укладываются в 12 человек — сходится.
В 2003 году было ~60%.
Т.е. мы пока одной ногой шагнули в новую эру) enterprise требует жертв. Но тренд понятен.
О причинах — часть уже назвал: Agile и микросервисы. Ещё — улучшившийся инструментарий: IDE и фреймворки/библиотеки под любую потребность. А сейчас ещё и AI.
Так что думаю, что к 6 людям в команде мы все скоро придём. Будет ли меньше? В теории да: ВП, аналитик, 1–2 разработчика, тестировщик.
P.S. Т.к. в таблицах процент команд, а не процент людей - в командах более 12 человек все еще работает более 50% разработчиков. Было - более 80%. Интересно кто это?
P.P.S. Станет ли медианным значением 1 — ВП, который с помощью AI пилит и тестирует сервисы сам? ...Где-то да, уже сейчас. Но массово — может помешать Skynet. Или поспособствовать — сидишь себе в матрице, вайб-кодишь с AI микросервисы.
#book_review #agile #ai #microservices
JetBrains: Developer Tools for Professionals and Teams
Software Developers Statistics 2024 - State of Developer Ecosystem Report
Explore key software developer statistics for 2024 in the State of Developer Ecosystem Report. Trends, insights, and tools shaping the developer world
👍1
Мы?)
Но что интересно - AI-шка здорово ускоряет создание таких часто одноразовых инструментов. Особенно если это shell скрипты или любой код не на основном языке разработчика. Так что подозреваю, что таких утилит будет больше, а времени на их создание будет затрачиваться меньше. И одноразовость уже не будет блокером.
P.S. Да, это снова флешбеки от Совершенного кода)
#book_review #ai
Но что интересно - AI-шка здорово ускоряет создание таких часто одноразовых инструментов. Особенно если это shell скрипты или любой код не на основном языке разработчика. Так что подозреваю, что таких утилит будет больше, а времени на их создание будет затрачиваться меньше. И одноразовость уже не будет блокером.
P.S. Да, это снова флешбеки от Совершенного кода)
#book_review #ai
🔥2