Pull to refresh

Comments 103

Сплошное ИМХО автора, возведенное в абсолют. С некоторыми тезисами я бы согласился, по некоторым начал «грызню». В общим, тролинг чистой воды.
Достаточно прочитать обзор, чтобы сказать что автор прав. Взять в пример общеизвестную проблему твитера, решение которой на самом деле элементарно (оно было здесь, в песочнице, пару лет назад, но парню так никто и не дал инвайт, потому что… сабж), просто оно идет вразрез с мнением большинства, и его никто не хочет замечать.
Я прочитал всю статью, а не только обзор.

Что здесь правда?
— 300 строк кода? Для меня и 25 много
— статики с изменяющимся состоянием? Попробуйте к такому проекту добавить логику о которой не предполагал автор (кода) — например еще один экземпляр SQL-фасада.
— Патерны. Согласен. Патерн не должен использоваться там, где нет необходимости в его конкретной фишки. И от патерна стоит избавиться там, где негатив от него превышает позитив.
— IoC. Согласен.
— Code first. В больших проектах, где к базе обращается 3-4 клиента разрабатываемых в разное время, разными группами. Когда у вас в компании 2 десятка баз синхронизируются друг с другом. Когда у вас есть таблицы с 1.5*10^9 строк. Удачи.

Как я и говорил автор не прав и ни неправ одновременно. У меня у самого есть воззрения достойные халевара, но я же не говорю что сообщество «не доросло до моего уровня, чтобы понять их глубину и проницательность».
Хотел оставить аналогичный комментарий.

Есть небольшой нюанс на счет IoC. Действительно часто его использование не оправдывается, так как реализации не подменяются в процессе жизни системы. Но профит заключается в том, что вы имеете возможность сделать это в любой момент. Никто не может дать гарантий, что завтра «стабильные» зависимости останутся стабильными. По крайней мере на основе личного опыта могу сказать, что можно предположить «условно стабильные» и «условно не стабильные» зависимости, но это все так условно…
Вместо того, чтобы делить зависимости на стабильные и нестабильные, лучше положить IoC в фундамент приложения и забыть о нем до тех пор, пока он не понадобится, если, конечно, он вообще понадобится. Это как-то проще что ли. В IoC само сложное — это понять принцип и причины. Если это понимание есть — остальное уже вторично. И не важно тонкий ли класс будет, толстый ли — замена любого из них происходит единообразно.
Так и живем…
Ты так ничего и не понял. Я говорю о том, что стереотипы мышления перерастают в слепоту, и пример с твитером привел не просто так. Люди голову ломают над решениями, выкидываются огромные деньги на их разработку и поиск, а какой-то пацан хотел это самое решение опубликовать здесь, на хабре. А вам всем оно было не интересно, потому что подход к программированию приводимый им не похож ни на одно существующее решение. У меня есть эта сохраненная страничка из песочницы, и я до сих пор не могу загрузить себе в мозг и прочувствовать тот алгоритм, который он из своего мозга выгрузил. Если бы каждый выгружал алгоритмы, а не использовал наброски других…

И не нужно придираться к деталям, я могу привести еще пять других примеров, если потребуется, где слепо следуют мнимым стандартам. Да каждый может привести такие примеры. Суть в том, что не нужно верить на слово, а проверять самому, и именно самому осознавать что и как лучше делать.

А три человека, которые минусанули мой предыщий комментарий, так же скептически относятся к чему-то новому. Я даже знаю как они думали. Раз твитер со своими миллиардами и умами не смог спроектировать удачное решение, то про пацана написан бред. Ну или пацан бред писал. Даже не в даваясь в подробности и ничего не спросив. Вот он стереотип мышления: все давным давно придумали — бери и используй. А вот хер, это всего лишь маркеттинг, и решения не вы выбираете, а вам их навязывают.

Топик — это не имхо автора, это реальность жизни. Жаль что только 74 человека это понимают.
Мне всегда нравились примеры с «пацанами»…

Современная технология разработки ПО направлена на решения проблемм сложности, а не на построение отдельных алгоритмов (для этого 5 лет ходят в ВУЗ).

Ни какой пацан, не зная проекта изнутри, ни когда не сможет сказать «что будет», если «зафигарить здесь полочку». Вспомните книги гуру — практикующих консультатов. Все их расказы начинаются с того, что они приходят на умирающий проект и пару дней разбираются с причинами — почему 10 человеколет еще не начали приносить прибыль. Гуру — знает об ограничениях своего восприятия, «пацан» — нет.

Идеи «пацанов» это не более чем одна из тысячи интерестных идей в мозговом штурме, которая должна еще пройти 5 фильтров и 5 трансформаций, чтобы стать возможно полезной.
А что за проблема твиттера?
Да-да, а что за общеизвестная проблема твиттера, и что за решение предлагалось?
БОльшая часть логики твитера реализована на клиенте. Такой подход делает пользование сайтом очень удобным и позволяет индексировать контент поисковыми системами. Для работы этого всего, в браузер каждый раз загружается js код больше двух мегабайт.

Решение предлагалось (не для твитера, а чтобы зарегистрироваться здесь, это мое мнение, что хорошо подошло бы для твитера) спецефическое, требующее отойти от общеизвестных шаблонов на начальном этапе проектирования системы, в результате чего можно сократить исходный js код, если отталкиваться от двух мегабайт, более чем в сто раз. Грубо говоря твитер может быть таким же внешне как и сейчас, работая на 20 килобайтах. Особенности поведения разных браузеров были так же были учтены.

Во всем этом меня привлекло то, что не нужно программировать отдельно клиентстую часть. Она как бы автоматически рождается. Это тяжело объяснить в трех словах, но в результате любое приложение можно сделать таким же приятным в пользовании как и твитер, достаточно писать лишь серверные скрипты на любом языке, и все это будет индексироваться поисковыми системами. Я реализовываю в свободное время этот механизм, на примере блога типа хабры. Просто у меня нет идей какой бы сервис реализовать, решил сделать блоговый движок. Закончу, обязательно напишу об этом тут, информации столько, что потянет на цикл статей.

А основа алгоритма заключается в том, что тег и все события js являются не представлением, а ядром, и какое приложение бы не строилось, писать этот тег и программировать события в шаблонах и js коде нельзя. Достаточно на них сослаться в нужных местах представления, и ядро само все сделает. Казалось бы абсурд…
На счет того, что первично а что вторично. Тест тоже могут быть первичны (TDD). Это такая же часть приложения как и остальной код. Но статика тоже может быть нужна, бесспорно.
И на счет IoC, если вы используете DI, то действительно лучше стараться инжектить все зависимости. Даже если не выделять какие-то интерфейсы. Главное не доходить до абсурда (как в вашем примере).
А еще на IoC можно практически полностью возложить обязанности менеджить жизненный цикл объектов.
автор пытается нас толсто троллить или он правда так думает?
>2. Статика – это плохо!
Статика это не плохо, плохо это глобальный стейт. Extension методы стейта не имеют, так что вполне кошерны.
1. Предлагаю для начала разобраться с понятием Domain Language и зачем оно в программировании.
2. Что вы понимаете под «статикой»? Статическую типизацию? Статические поля классов? Статические ассеты в веб-приложениях? Раздел механики?
3. Согласен.
4. Скажите, а вы пишете тесты?
5. Согласен.
Слышал только один сколько нибудь весомый аргумент – статику сложнее покрыть тестами.
Почему?
Скорее сложнее покрыть тестами места её использования.
По первому пункту: если вам «повезло» работать с коллегой, который пишет короткий, но ничего не значащий код, это не означает, что писать длинные методы — хорошо. Это значит, что ваш коллега не потрудился написать понятный код, это раз.

Слышали что-нибудь про декомпозицию? Если вы не понимаете, что делает маленький кусок кода, то понять кусок, состоящий из 10 маленьких непонятных кусков — еще сложнее, это два.
Проблема в том, что поделить код на 10 адекватных кусочков, дать вменяемые имена методам, написать вменяемую документацию, обычно сильно сложнее чем написать один большой, более-менее качественный метод. Поэтому на практике чаще всего «не везет». Далее мы имеем какую-нить длинную цепочку неадекватных вызовов и прыжки по файлам, когда доходя до конца цепочки уже забываешь с чего начиналось.
Для Вас может и сложнее, но не все же такие…
Странное утверждение за всех.
С IoC (контейнер, DI или ещё что — не суть) спорно. Несколько раз сталкивался с тем, что нужно уменьшить связность, причём о такой необходимости и мысли не возникало. С тех пор как-то стараюсь если не навороченные контейнеры использовать, то хоть на один уровень поднимать создание объекта и инжектить его конструктором/сеттером. Как правило получается не на один (рекурсия) а до первого ветвления где объект может не понадобиться.
UFO just landed and posted this here
Заголовки не обёрнуты тегами иронии.
Не могу воспринимать это всерьез, троллинг чистой воды.
Есть здравые мысли, но примеры, которые Вы приводите, никуда не годятся!
Из сказанного Вами, у меня сложилось впечатление, что Вы либо недавно работаете в индустрии и еще не представляете как развивается код во времени, либо Вы работали только над маленькими/простыми приложениями в небольших командах.
> если обстоятельства складываются так, что ваш метод не может быть меньше 300 строк
Значит вы профнепригодны и не можете разделить код на простые слабосвязаные сущности
Я тоже раньше думал с такими же категоричностью и максимализмом. Пока жизнь не показала ситуации, когда код метода в 500 строк оказывался на порядок удобнее для сопровождения и понимания. Нашим программистам даже пришлось в некоторых местах собирать код нескольких методов обратно в один метод — чтобы удобнее работать было.
«Не бывает правил без исключений, включая данное правило». (С) Не мой.

Кстати, и развитие человечества часто идет по следующему пути: что-то сначала кажется универсальным и очевидным, имеющим бесконечные границы (правило на все случаи жизни), а через некоторое время кто-то натыкается на границы применимости и «Прощай, догма!».
Дайте пример, если не сложно.
Я около 10 лет в программировании от антивирусов до игр и таких случаев не встречал.
24 года профессионального, т.е. за деньги программирования. Начиная с ЕС ЭВМ.

А теперь по делу:
1. Кол-во строк кода зависит от языка программирования. Когда слышу об ограничениях 15-20-30 строк в методе, становится смешно — у нас часто одна SQL-команда имеет больше размер.
2. Манера написания кода зависит от преследуемых целей. Если «Лишь бы заработало (написал-сдал-забыл)», то это одно. Если «Чтобы потом проще было 100500 раз модифицировать», то совсем другое. А если главное — как можно быстрее находить и устранять ошибки в практически незнакомом коде в режиме жесткого цейтнота, то это уже третье.

Теперь представьте ситуацию:
— Большой проект (сотни тысяч строк кода).
— Работа с базами данных (см. про размер команд).
— Допустимый срок устранения ошибок — максимум час-другой (обычно не более 20-30 минут) силами одного(!) дежурного программиста. Соответственно, на большинство кода он смотрит, будто видит в первый раз (даже если он сам его написал полгода назад).
— Произошла ошибка, характер которой пока неясен: то ли ошибка в постановке, то ли в алгоритме, то ли защита от дурака не всё выловила, а может просто непонятный сбой какой-то произошел и т.д. Т.е. по ходу дела нужно вникать в ситуацию.

По своему опыту скажу, что локализация места ошибки с точностью до функции предметного уровня обычно проходит достаточно быстро. А дальше наступает черед уровня языка программирования. И от того, как представлена эта предметная функция — одним куском или развесистой иерархией мелких методов — сильно зависит скорость и время разборки с проблемой.
Оперативная память в мозгах у программиста не резиновая (а кэш еще меньше — слышали, наверное, о 7+-2?), а время ограничено. Что такое каждый дополнительный метод? Это дополнительная информация как минимум в виде:
— названия метода
— списка и порядка его параметров
— позиции этого метода в последовательности вызовов
— позиции этого метода в иерархии других методов.
А теперь представьте, что искусственно созданных на ровном месте методов десятки и образуют они многоуровневое дерево (правильная с точки зрения теории декомпозиция). Всё — выгрузка в своп обеспечена! А это — дополнительные затраты сил и времени. В то время, когда можно было бы обойтись без них.
Есть и другие моменты. Например, отслеживание, где присваиваются/меняются поля таблиц из искомого вами списка. Особенно если учесть, что таких полей может быть несколько и в SELECT SQL допускается неявное присвоение им значений. Даже если текстовый редактор при поиске забросит вас в какой-нибудь метод, вам это слабо поможет, т.к. сам найденный код еще ничего не значит — для анализа вам придется сначала сориентироваться: когда, откуда и при каких обстоятельствах этот метод вызывается, есть ли до его вызова другие присвоения искомым полям и т.д. В рамках единого последовательного куска кода таких проблем нет — скользишь взглядом сверху вниз и последовательно видишь весь ход процесса, попутно отслеживая нужные имена.
Есть и другие нюансы, которые слету не вспомнить. Можете поверить на слово: программисты, которые работают с проектом такого уровня и способны в столь короткое время локализовывать проблемы, — далеко не чайники. И профнепригодными в плане декомпозиции их не назовешь, раз они ее сначала сделали, а потом часть методов всё же решили собрать в один большой. И уж поверьте: раз эти программисты, поработав с «правильной» декомпозицией какой-то предметной функции, заменили ее на единый кусок кода, то им было с чем сравнивать и были серьезные доводы для такого шага.

Я не агитирую за написание кода единым куском.
Я просто хочу еще раз напомнить, что у всякого правила бывают исключения.
В нашем случае это код, который обладает следующими характеристиками:
— длинный, но последовательно выполняемый (без ветвлений и циклов)
— никакая его часть нигде больше не используется
— с предметной точки зрения выполняет одну функцию
— требует максимально быстрого поиска и устранения ошибок.
Вот такой код действительно легче обслуживается единым куском — без разбиения на иерархию методов.
У нас подобных участков кода крайне мало (наверное, доли процента), но они есть.

P.S. Ситуация схожа с командой GOTO. Я помню времена, когда ее чуть ли не анафеме предали. Потом оказалось, что в отдельных случаях грамотнее делать всё-таки с GOTO (если язык позволяет), чем на ровном месте плодить кучу флагов.
Простите, но я не понял в чем суть то статьи, если конечно все в ней серьезно.
Только несколько базовых идей об удобстве написание когда и его сопровождения,
ну и немного личного мнения автора
Весь смысл статьи для меня уложился в банальные:
-не пишите большие методы, их трудно сопровождать
-статика это якобы всегда плохо
-паттерны везде подряд, где не надо это то же не хорошо

К сожалению я так и не понял, при чем тут стереотипность мышления то?
Т.е. если обстоятельства складываются так, что ваш метод не может быть меньше 300 строк, не стоит его дробить на отдельные методы. Названия таких методов ничего не скажут разработчику.

Я с трудом представляю себе операцию (за исключением массовых присвоений, которые надо перекладывать на маппер все равно), которая занимает больше 300 строк и при этом не дробится на подоперации.

Мораль: если у вас код не влезает в экран (хорошо, два экрана) — это повод задуматься, правильно ли у вас декомпонована задача. Code smell.

Иметь возможность легко подменять одну реализацию на другую — это здорово! Это действительно здорово. Но только в том случае если вы видите ситуацию, при которой вам это может понадобиться.

Такая ситуация есть в 99% случаев, и она называется «unit-тестирование».

(При этом никто не говорит, что подмена должна быть в конфигурации)

Ну а про Code First совершенно отдельный разговор. Не надо смешивать несколько вещей:
(1) собственно Code First (в любом его виде, когда структура БД генерится на основе программного кода)
(2) your code is your model (когда маппинг на БД описывается кодом, а не xml или другой моделью)
(3) EF Code First, который частично реализует первое, а частично второе.

Я, например, всегда любил второе, и поэтому когда появился EF/CF радостно на него перешел (с некоторых других, иногда самописных технологий). Но при этом я не использую ту часть EF/CF, которая создает/меняет БД по модели, описанной в коде, поэтому программа не управляет БД.
Хороший пример такой вещи, которая плохо дробится — использование Detached criteria в Hibernate. Вернее его формирование

Отрефакторите мне этот метод таким образом, что бы это реально принесло смысл. Я серьезно. Я долго бился над этой проблемой, но решения не нашел:
    public DetachedCriteria build() {
        final DetachedCriteria criteria = DetachedCriteria.forClass(Item.class);
        criteria.add(eq("singleItem", singleItem));
        if (itemId != null) {
            criteria.add(eq("id", itemId));
        }
        if (StringUtils.isNotEmpty(itemName)) {
            criteria.add(ilike("name", wildcard(itemName)));
        }
        if (itemSalePrice != null) {
            criteria.add(eq("salePrice", itemSalePrice));
        }
        if (itemStorePrice != null) {
            criteria.add(eq("storePrice", itemStorePrice));
        }
        if (StringUtils.isNotEmpty(itemProducer)) {
            criteria.add(ilike("producer", wildcard(itemProducer)));
        }
        if (StringUtils.isNotEmpty(itemProducerCode)) {
            criteria.add(ilike("producerCode", wildcard(itemProducerCode)));
        }
        if (StringUtils.isNotEmpty(itemDescription)) {
            criteria.add(ilike("description", wildcard(itemDescription)));
        }
        if (StringUtils.isNotEmpty(itemSerialNo)) {
            criteria.add(ilike("serialNo", wildcard(itemSerialNo)));
        }
        if (supplierEnterpriseId != null) {
            criteria.add(sqlRestriction("{alias}.supplier_enterprise_fk = ?", supplierEnterpriseId, BIG_INTEGER));
        }
        if (itemTypeId != null) {
            criteria.add(sqlRestriction("{alias}.item_type_fk = ?", itemTypeId, BIG_INTEGER));
        }
        if (upperItemId != null) {
            criteria.add(sqlRestriction("{alias}.upper_item_fk = ?", upperItemId, BIG_INTEGER));
        }
        if (unitTypeId != null) {
            criteria.add(sqlRestriction("{alias}.unit_type_fk = ?", unitTypeId, BIG_INTEGER));
        }
        if (!attributes.isEmpty()) {
            addAttributeSearchConditions(criteria);
        }

        criteria.addOrder(Order.asc("id"));
        return criteria;
    }

Приведенный вами пример — это по категории «массовые присвоения», много совершенно однотипных строчек. Если я правильно понимаю, что делает ваш код, и из чего он построен, я бы делал это на основе reflection (или кодогенерации), просто чтобы не ошибиться в куче строк ручного кода.
Я видел такие просты решения через reflection. Вообще отражение — это зло, которое должно использоваться В ИСКЛЮЧИТЕЛЬНЫХ случаях. Написать просто, а вот поддерживать очень сложно. Мне приходилось через такой код построенный на «отражениях» продираться НЕДЕЛЮ. Пока не нашел причину. Когда я ее нашел, я подошел с красными глазами к товарищу, кто это написал и спросил «зачем?» на что получил ответ старый как мир: «так было проще».
Вообще отражение — это зло, которое должно использоваться В ИСКЛЮЧИТЕЛЬНЫХ случаях.

Стереотип?

Отражение — это не зло, а инструмент, такой же, как и все остальные в языке/платформе. Надо понимать его сильные и слабые места. Для задачи «а давайте скопируем все свойства из этого объекта в свойства с таким же именем в другом объекте» — сложно найти что-то лучше. Все декларативное программирование с помощью атрибутов в .net построено именно на reflection.
Одно дело написание какого-то сверхгибкого фреймворка и декларативное программирование, другое дело использование его без надобности. Это что называется очень часто «write-only» подход. Если у тебя обвешан объект проксями и т.п, а ты пытаешься на прямую ему филды посеттить которые помечены как private. Ну зачем это через отражение делать. Что бы потом отлавливать баги там, где 99% населения их не отлавливает?

И кстати то, что я описал — это НЕ тупое копирвоание свойств из одного объекта в другой. Это приведение объекта в нужное состояние. Через отражение это сделать можно. Но зачем усложнять и без того сложные вещи? Это как забивать гвозди микроскопом — не удобно, не практично и поломкоопасно.
Если у тебя обвешан объект проксями и т.п, а ты пытаешься на прямую ему филды посеттить которые помечены как private.

Я такого делать и не предлагаю.

И кстати то, что я описал — это НЕ тупое копирвоание свойств из одного объекта в другой. Это приведение объекта в нужное состояние.

… по заданному и понятному алгоритму. Если это можно переложить на компьютер, это надо переложить на компьютер.

Но зачем усложнять и без того сложные вещи?

Да нет в этом коде ничего сложного. Вся его «сложность» — из-за множества почти одинаковых операций.
Первое что в голову приходит заменить
if (condition) {
  criteria.add(criteria_expr);
}

на что-то вроде
add_critera_if(condition, expr);


да и ещё копипаст наблюдается, например с sqlRestriction
где здесь копипаст? Ткните конкретно. А что хорошего в add_critera_if. С тем же успехом можно было бы выкинуть скобочки и заинлайнить. Проку с этого мало, если он вообще есть.
А что хорошего в add_critera_if

Экономите лишнюю повторяющуюся строчку. Код станет где-то в 1.8 раза короче.
с тем же самым успехом я мог бы писать:

if (itemTypeId != null) criteria.add(sqlRestriction("{alias}.item_type_fk = ?", itemTypeId, BIG_INTEGER));

и тем самым тоже сэкономлю строчку кода.
К тому же типы аргументов будут разными, соотвественно проверки нужные тоже разные. И т.д. и т.п. В общем из простого но не очень компактного кода получатся макароны, которые сложнее читать.
Короче ко всему стоит подходить с головой.
Вы правда не видите разницы между вашим кодом и
criteria.addIfNotNull(sqlRestriction("{alias}.item_type_fk = ?", itemTypeId, BIG_INTEGER)

?

Жаль, если так. Для меня второй намного читаемее.

В общем из простого но не очень компактного кода получатся макароны, которые сложнее читать.

Вот как раз если подходить с головой, то получится читаемо. Потому что явно видно намерение. Пример я привел.
DetachedCriteria — это класс фреймворка. Враппить его в другой класс и делать тучу методов оберток ради того, что бы ОДИН раз использовать — не понятно, есть ли в этом большой смысл. Я конечно понимаю, что многие девелоперы стремятся к совершенству, совершенному коду и т.д. и т.п., Но как известно на другой стороне — девелоперы любят УСЛОЖНЯТЬ. А потом получаются очень сложные системы, дорогие в поддержке, и т.д. и т.п. И это называется Overengineering. Это как рассказ о том, что американцы вложили миллионы долларов на то, что бы создать ручку, которая будет писать в космосе в то время, как русские просто взяли карандаш.
UFO just landed and posted this here
Враппить его в другой класс и делать тучу методов оберток

… как тяжело людям жить без extension methods. И не тучу методов, а два-три, в зависимости от системы типов.

ради того, что бы ОДИН раз использовать

Я привык думать, что критерии — это распространенный шаблон в системе с доступом к данным.
не знаю, тяжело или нет без extension methods. Но это данность, с которой мне придется мириться.
Ну, extension methods — не единственный способ решать такие проблемы.
UFO just landed and posted this here
Да, в этом коде, к сожалению, много ошибок. Но общая идея, на самом деле, в том, чтобы передавать во враппер create_if* ограничение, которое нужно создать, и параметр для этого ограничения (а уже враппер проверят параметр на актуальность и создает ограничение). Способов это сделать — больше одного, и зависит от конкретной реализации самого класса критериев и возможностей языка.
В этом коде много ошибок для людей, кто придерживается вашей точки зрения. Этот код компилируется и даже (сюрприз-сюрприз) работает.
понял, что написал не проследил эту структуру ответа. думал, это относится к моему куску кода. комменты не валидны ;-0)
вы не подумали о том, что это филды класса и что я его скопировал?
*скопировал кусок кода не полностью из класса

Все становятся такие умные и с умным видом говорят «в этом классе не хватает закрывающей скобочки» думая про себя «какой я умный, а тот кто написал комментарий лох и нуб» вместо того что бы подумать на шаг вперед ;)
понял, что написал не проследил эту структуру ответа. думал, это относится к моему куску кода. комменты не валидны ;-0)
UFO just landed and posted this here
Как это сделать в Java: «И убрал бы != null»? :)
if (И_убрал_бы) {
    И_послал_бы();
}
UFO just landed and posted this here
Ну вообще-то можно, если бы это был Boolean. Но этот путь попахивает NPE, так что… :)
UFO just landed and posted this here
Там можно, например выделить код распаковки архива в отдельную функцию, но это бессмысленно, потому что она больше нигде не используется.

Функции выделают не только потому, что они где-то еще используются, но еще и чтобы подчеркнуть намерения кода.

В вашем случае просматривается отчетливое разделение на сценарий выполнения (нашли, нашли, открыли, проверили-распаковали и так далее) и собственно его элементы.

Я не говорю, что это обязательный критерий. Я говорю, что длинная функция — вероятный признак неудобочитаемого кода.
UFO just landed and posted this here
Настоящая функция — это та, которая действительно используется повторно либо имеет традиционный функционал (граммар-наци, идите в жопу), например, выделение памяти и инициализация объекта.

Интересно, откуда вы взяли такое определение?

В моем случае пришлось бы сочинять псевдо-функции, которые используются один раз и выделены только ради внешней компактности. На мой взгляд, функции ради компактности — зло.

Не компактности, а читаемости. Это разные вещи.
UFO just landed and posted this here
функция должна делать что-то одно, при этом подразумевается «одно» в человеческом смысле, образующее одну операцию в голове разработчика.

SRP в чистом виде.

Вы же вот чуть выше именно ради компактности ввели новый метод criteria.addIfNotNull, хотя там и оригинальный код достаточно читаем.

После того, как пообщаешься с DSL, начинаешь понимать, что он не так уж читаем, как хотелось бы. В частности, каждый if в коде, грубо говоря, понижает читаемость на один попугай. Намерение кода очевиднее из вызова «добавитьКритерийЕслиОнНеПуст», чем из вызова «если критерий не пуст, то добавить Критерий» (как бы нелепо это не звучало по-русски).

Dense code, в чистом виде.
UFO just landed and posted this here
Во-первых, если написать метод правильно, то он будет выглядеть, скажем, так:

В правильной реализации не должно быть повтора itemTypeId.

вон, парой строк выше проверяется isNotEmpty, а в конце — isEmpty

Наглядная демонстрация того, о чем я говорю. В конце проверяется !isEmpty. Но в куче визуального одинакового кода вы это пропустили.

Писать для них отдельные методы?

В реальности там ровно один метод: для nullable он проверяет, что они не null, для строк — что они не пустые. С точки зрения условия (и логики) он один и тот же, решается это перегрузками. И на самом деле, последнюю строчку (про атрибуты) на него переписывать не надо.

Можно переделать метод в более общий — criteria.addIf(<выражение>, <атрибут>)

А вот этого как раз делать не надо. Так вы не выражаете намерение вашего кода (добавлять условия только тогда, когда они заданы), а просто переформулируете уже существующий код.

Не удивительно, что вам самому кажется, что это неправильно.
UFO just landed and posted this here
Должен быть метод isEmpty.

Кому должен? В .net вот есть метод String.IsNullOrWhiteSpace, так вот, я его в отрицании вижу в несколько раз чаще, чем в прямом применении. А это означает, что будут ошибки (и они есть, регулярно).

А как универсальный метод будет знать, какое условие добавить?

Вот как раз (условие) критерий туда надо передавать.

И да, я не вижу никакой принципиальной разницы между addIfNotNull и addIf.

А зря, потому что первая описывает намерение, а вторая — алгоритм.

На мой взгляд, addIfNotNull даже хуже, потому что неточно описывает поведение метода для строк.

Название неверно выбрано. Должно быть addIfSpecified.
UFO just landed and posted this here
Методы без слова not удобнее, потому что когда нужно проверить противположное условие, приходится городить огород из двух отрицаний if (!my.attrIsNotSet).

В DSL в таких случаях делают два метода.

То есть, по вашему, addIf недостаточно ясно выражает намерения кода и описывает алгоритм, а addIfSpecified — достаточно? :) При этом сам алгоритм внутри предполагается одинаковым, разница в названии.

Алгоритм разный. AddIf предполагает два параметра, один — критерий, а второй — условие, при верности которого будет добавлен критерий. AddIfSpecified предполагает два параметра, один — критерий, а второй — значение, при «заданности» (не null, не пустая строка) которого критерий будет им параметризован и добавлен.
if (not(myString.IsEmpty())) попахивает NPE, так как myString в базовом случае не инициализирован. Потому и используется isNotEmpty
Я не замечаю. В первом случае мы используем две (а, как правило, три — ещё {} ) конструкции языка, а во втором одну.

Ну и чисто семантически, по-моему, вынос add вперёд более логичен — основная задача этого куска кода добавить атрибут, а не проверить выражение.
UFO just landed and posted this here
Скорее semantic sugar. Был бы другой язык можно было написать
criteria.add(<аттрибут>) if <выражение>

и не было бы этой ветки :)
В Boo можно. Но при этом даже там это используется далеко не всегда.
Я Ruby имел ввиду. Но в этом случае напрашивается, имхо.
Не напрашивается. Весь смысл «оптимизации», предлагаемой в этой ветке, в том, чтобы в коде при его чтении не было условий.
Чтоб условия не мешали чтению.
Когда их нет, они и не мешают, логично?
Даже одноразовые функции выделяют по смыслу, просто чтоб читать было понятнее.

В книгах по идее тоже нет повторяющегося текста, однако там есть главы, параграфы и куча разных видов другого смыслового разбиения… А Вы так все 350 строк махом… Да еще и без комментариев. Сочувствую.
UFO just landed and posted this here
Со всеми заголовками стереотипов согласен — автор просто бросается в крайности.
2. — Как раз хорошо считать, что тесты — это первично.
Автор тролль и зареган вчера всего лишь
Краткий конспект статьи: всё хорошо в меру.
Всегда думал, что хорошая аргументация рушит любые стереотипы. Применительно в конкретном проекте конечно же.
Считать, что стереотипы это плохо — плохой стереотип.
Ко всему нужно подходить со здоровой долей прагматизма и включенной головой. А не «я буду использовать это потому, что я прочитал это в такой-то книге от такого-то клевого автора».
Код метода должен вмещаться на экран монитора.
Сначала подумал, что имеется ввиду, что в ширину (как в руби или пайтоне 80 символов), только потом дошло, что в высоту.

Кстати, насчет высоты еще ладно, если сильно надо или лень, то большой метод или функция норм. Но я не понимаю людей, которые пишут строчки по 100-150 символов. Мало того, что в экран не влезает и приходится прокручивать, так ведь и 2 файла одновременно не откроешь.
Применяя какой-либо принцип, нужно четко понимать, зачем этот принцип нужен. Применяя что-то только потому, что прочитал об этом в книге, или гуру сказал — это и приводит к тому, что описано в статье.
Рекомендую автору почитать следующие книги:

Mark Seemann — «Dependency Injection in .NET»
Стив Макконнелл — «Совершенный код»
Роберт Мартин — «Чистый код»
Всё правильно написано. Что код новичка, что код «У меня программирование головного мозга» — одинаково плохо. Строя дом, не нужно делать многоэтажный шалаш, и не нужно делать насыпи для возведения пирамиды Хеопса.
Вообще пост похож на шутку. Автор ржёт?
А что имеется ввиду в пункте про статику, можно поподробнее и с примером? Использование статических классов с, соответственно, статическими методами? И как это связано с тестами (типа в тестах можно было бы создать экземпляр класса и погонять, а если класс статический, то не получится?) и расширяемостью? И, к слову, я несколько раз видел такое: «синглтоны использовать нельзя, т.к. потом тестировать сложно» и тоже не вник в суть проблемы (хоть это и не про статику). Но я и тестами не пользуюсь и в общем не в теме TDD, юнит-тестов и прочего, если эти практики не используются, то проблемы нет? И что такое extention-методы?

Про статику интересно еще и потому, что я сам часто использую статические классы там, где мне нужен какой-то «отстраненный функционал» и я знаю, что мне нет нужды создавать экземпляры этого класса, а воротить, например, синглтоны не хочется, получается «тяжеловеснее» и некрасивее, а зачем — не понятно. С другой стороны, если таких классов набирается пяток, то закрадывается чувство, что что-то я делаю не так и все это может превратиться в дурнопахнущего монстра.
Скорее использование статических переменных класса и статических методов. Статические переменные класса по сути являются глобальными переменными. Их недостатки не надо рассказывать? Статические методы работающие с ними в ту же степь. Если статический метод чистая функция, то проблем нет, пока не доходим до тестирования. При тестировании зачастую полезно подставлять вместо полноценных объектов фэйковые, не делающие ничего полезного кроме, максимум, возврата значений. Для этого обычно используют различные техники IoC. Но при обращении к статическим членам класса их, как правило, просто не подменить — обращение к статическому методу это жесткая зависимость практически всегда. Как подменить где-то в дебрях тестируемого метода вызов типа SomeClass::someMethod()? Был бы someObj->someMethod(), где SomeClass obj параметр метода или конструктора, проблем бы почти не было — унаследоваться от someClass, переопределить somemethod на пустой и передать его параметром. Название же класса параметром не передать, из параметра его не вызвать. По крайней мере простым способом.
Сингл тоны та же проблема, ведь в их реализации используются статические переменные и методы.
Сингл тоны та же проблема, ведь в их реализации используются статические переменные и методы.

На самом деле, нет. Если в типовой реализации синглтона использовали статики, это еще не значит, что сейчас, при таком засилии IoC/DI продолжают использовать статики.
Хотел же написать в «типовой реализации», но лень стало :) В любом случае автор, грубо говоря, призывает от IoC отказаться, а статики использовать, так что тестировать будет приятно…
Автор комментария, на который вы отвечает, не тестирует. Поэтому ему все это не особо важно.
Любой разработчик выше мидла негативно отреагирует на такое:
Как? Я ведь именно за это получаю большие деньги! Именно поэтому я такой крутой специалист! Если я буду писать более простой код — то у меня не будет преимуществ перед менее опытным разработчиком.

И в итоге рождаются проекты по 200 000 строк кода, у которых «вах, пэрсык, а не ядро», покрытых на 95% тестами, которые можно было реализовать быстрее и проще, но это уже мечта ПМ-ов, а не разработчиков.
Sign up to leave a comment.

Articles