Pull to refresh
8
0
Толмачёв Дмитрий @FiresShadow

Разработчик ПО

Send message
«опыт показывает, чем длиннее метод, тем выше вероятность, что писал на скорость»
Вообще вся статья и написана ради того, чтобы показать что длинные методы, как правило плохо, но — НЕ ВСЕГДА (неужели по первым трём предложениям статьи этого непонятно?!)

«Вынесите его во внешний файл»
Варианты с внешним DSL и нейросетью уже обсуждались РАНЕЕ(да и статья СОВСЕМ НЕ ОБ ЭТОМ). Пожалуйста, вставляйте свои предложения и пожелания в КОНЕЦ обсуждения, а не в середину, тем более что ваш коммент имеет мало что общего с тем местом, куда вы его вставили. Мне бы хотелось тратить время на ответы действительно заинтересованным людям, а не тем, кто не прочитал ни статьи, ни комментарии к ней…
«Я не думаю, что это реалистичная модель закачика, не знающего умножения.» — вы сами предложили модель заказчика не знающего умножения, но использующего много математических операций…
«Нельзя ли предложить заказчику более эффективную теорию его предметной области» — вы действительно хотите объяснять экономистам и банкирам более абстрактную версию экономики, а физикам-ядерщикам — более удобную и абстрактную модель атома, просто потому, что «программисты часто обладают более развитым абстрактным мышлением»? Я так не думаю. Даже если вы умнее банкиров, они не будут в восторге от того, что вместо того, чтобы облегчать их жизнь, вы её усложняете, навязывая им неудобные для них, но удобные для вас модели.
«класс должен делать что то одно»
На самом деле, не существует сущности, делающей только одно действие (если она состоит более чем из одной примитивной команды языка). Однако существуют сущности, делающие одну операцию НА НЕКОТОРОМ УРОВНЕ абстракции. На одном уровне абстракции, некоторый метод может производить два действия: скачивать и парсить, на другом уровне абстракции одну операцию — получить данные, на третьем уровне абстракции — миллион операций. Уровень с одной операцией более абстрактный, т.к. вам не важно, качаете вы страницы, используете API, или реплицируете базу данных. И название метода " Узнать информацию о лотах закупки" вполне говорит о том, на каком уровне абстракции он лежит. И на этом уровне абстракции у метода ОДНА область ответственности.
Этот пример был приведён для того, чтобы проиллюстрировать тот факт, что метод, внутри которого находится функция «Узнать информацию о лотах закупки» может раздуваться до больших размеров при раздутии доменной модели без возникновения запахов «стрельба дробью» и «расходящиеся модификации». А вот если вы разобьёте метод, в котором содержится метод «Узнать информацию о лотах закупки» на несколько методов, то возникнет запах «стрельба дробью» (относительно методов).
В статье я ничего не говорил о внутреннем устройстве этого метода и сам метод написан на псевдокоде, потому что частности его реализации не важны для демонстрации этого факта. «Узнать информацию о лотах закупки» может быть как НекоторыйПолиморфныйОбъект.Узнать(), так и просто вызов некоторой функции — это уже зависит от размера содержимого метода. Если там внутри пара строчек кода, то соблюдая KISS и high cohesion, раскидываем код по методам, а не по классам. А если внутри много кода, то выносим в отдельный класс. Вообще я предпочитаю метод и класс объединять термином «сущность».

«В вашем примере должно быть два класса.»
Конечно, класс Скачивальщик должен присутствовать, но он используется внутри функции «Узнать информацию о лотах закупки». Он мог бы и не использоваться (если бы мы обращались к API сайта). Но нас это не интересует. Нас интересует метод, псевдокод которого приведён в статье
Мне непонятно, чем массив из указателей (без применения DSL) лучше непосредственного вызова самих методов. Мне кажется, что при таком подходе будет сложно вносить изменения в список параметров, сложнее отлаживать, да и разбираться в коде при таком подходе тоже капельку сложнее. А какие преимущества у данного подхода? Чем это лучше? Тем, что не смущает количеством строк в методе, или есть какая то ещё причина?
В статье есть более длинный пример — аборигенский танец из ста хаотичных безсистемных действий. Есть обоснование почему не удастся выделить эти безсистемные действия в отдельные функции. Пожалуйста, перечитайте статью ещё раз, только внимательнее.
Если тот, кто выдаёт вам user-story (спецификацию программы), не умеет умножать, действительно следует задуматься о том, чтобы завести отдельную функцию «Сложить m между собой n раз», а внутри этой функции использовать умножение. Умный JIT-компилятор оптимизирует ваш код, в результате чего быстродействие не пострадает. В C++ можно объявить метод как inline. При наличии такого метода, вам не придётся напрягать лишнюю извилину мозга, когда заказчик произносит: «и тут нужно сложить степень двойки этого полиномиального коэффициента между собой записанное в таком-то файле количество раз». Вы просто вызовете нужную функцию для вашего уже вычисленного коэффициента и только что прочитанного значения из файла. Однако умножение — очень примитивная абстракция, выражающая через другую очень примитивную абстракцию (сложение). Если же вам придётся переводить сложную абстракцию постановщика user-story в непонятно для чего изобретённую вашу сложную абстракцию, то ваш мозг быстро закипит. В результате вы напишите гораздо меньше кода с гораздо большим количеством ошибок. Советую вам почитать книгу о DDD Эрика Эванса.
Вы предлагаете использовать внешний DSL. Об этом написано много статей на хабре. Т.е. где-то в коде описано как нужно приседать, и когда где-нибудь в конфигурационном файле встречается команда «присесть», вызывается код, который «знает» как приседать. Основная проблема в том, что не всё уместно реализовывать через DSL. На практике принято сочетать внешний DSL с обычным кодом, причём обычного кода как-правило на практике получается больше. Почему так — тема отдельной статьи. Повторюсь, что единственная цель этой статьи — развеять миф о том, что если в коде метода более чем n или менее чем m строк кода, то с методом ТОЧНО что-то не в порядке в плане проектирования. Разумеется, этот код можно сделать и лучше, и по-другому. Можно вообще завести нейронную сеть, которая будет в интернете скачивать картинки приседаний и учиться приседать автоматически. Но статья совсем не об этом. Можно сказать, что танцы никому не нужны; да и парсить сайт тоже не нужно, а нужно заставить разработчиков сайта написать API. Статья тоже не об этом. Указанные алгоритмы приведены как примеры. Хочется услышать ваше мнение (желательно обоснованное) по поводу того, миф это или нет. Однако за комментарий спасибо. Было бы ещё лучше, если бы вы указали название предлагаемой методологии (внешний DSL), чтобы те, кто не в курсе, могли более подробно с ней ознакомиться
«А можно немного конкретики?»
Я как раз и постарался привести пример на псевдокоде, когда все действия находятся на одном уровне абстракции и не удастся разбить их на методы.

«А еще правильнее было бы не парсить страницы, а использовать API этого сайта-поставщика.»
А ещё лучше жилось бы, если бы база данных сайта-поставщика автоматически реплицировалась на исходную, но это не всегда возможно, да и не все сайты предоставляют айпи.
Лоты это часть закупки. Внесите лоты в закупку. После этого внутри parseЗакупка будет тот псевдокод, который описан в статье. Я не считаю хорошей мыслью разбивать закупку на два класса. Прочитайте пожалуйста обоснование в ответе на комментарий risik-а, который предлагает проделать тоже, что и вы, но только не с лотами, а с поставщиком. Не хочу засорять статью однотипными комментариями :)
а)Здесь 5 строк кода. Это как раз меньше 10, но больше 3.

После перечисления этих 5ти методов написано… и ещё подобные методы, просто я не стал перегружать статью, отметив, что процесс торговли можно всё усложнять и усложнять, в результате чего количество сущностей на одном уровне абстракции разрастётся, разрастётся и метод, содержащий сейчас 5 строк кода. Можно наращивать метод неограниченно.

б)Навскидку в данном конкретном случае я вижу разбиение на две процедуры: 1) Узнать информацию о поставщике 2) Узнать информацию о закупке.

Нам нужно распарсивать информацию о поставщике, относящемуся к закупке. Внутри закупки есть ссылка на поставщика. Таким образом, поставщик — часть информации о закупке. Если разбить код на эти две функции, то когда через месяц, немного подзабыв код, вы снова его откроете, то будете неприятно удивлены, не обнаружив парсинг поставщика в функции «Узнать информацию о закупке». Согласитесь, что продавец — часть информации об процессе обмене ценностями между двумя людьми (организациями). Имя же функции «Узнать информацию обо всём в закупке, кроме поставщика», которое здесь уместно, лично мне кажется не лучшим выбором.

«Количество строк в методе не является самоцелью. Это всего лишь индикатор.»

Абсолютно с вами согласен. Я как раз и писал о том, что этот индикатор не всегда показывает корректные данные
На самом деле, «Узнать информацию о поставщике» — это название короткого метода. Но вот в методе, в котором этот короткий метод используется, таких коротких методов может быть много. Признаю свою ошибку — программисты плохо воспринимают псевдокод, внесу правки в статью чтобы было понятно что это названия методов
На самом деле, вы поняли немного неправильно. Аукцион, лоты, протоколы, дата изменения закупки — всё это относится к закупке. Лоты не относятся к аукциону. Все эти сущности лежат на одном уровне абстракции. А из-за того, что они на одном уровне, и действия по их обработке также на одном уровне абстракции, то их и не удаётся скомпоновать в разные функции. Этот пример был специально так подобран. Это и есть реальный пример, только на псевдокоде
В этой статье я не ставил целью доказать что большой метод — это хорошо. Я пытался развеять миф, что «если ваш метод больше n строк кода, то с ним что-то не в порядке». С вашим утверждением я согласен, однако хочу добавить, что пытаться всегда быть где-то посередине — это тоже своего рода крайность.
Вы правы, я действительно имел ввиду, что длина метода напрямую зависит от количества составных шагов в доменной модели для данной user story. Также я согласен с вами, что накручивание синтетических абстракций поверх доменной модели приведёт лишь к отходу от единого языка (Ubiquitous Language), которым пользуются как специалисты предметной области, так и программисты, что в итоге сведёт на ноль использование DDD в проекте и усложнит сам проект. Однако доменная модель в зачаточном состоянии используется и теми, кто даже ни разу не слышал о DDD. Проектируя программу в стиле ООП, программист является отчасти и специалистом предметной области, пытаясь разбить программу на модули, соответствующие некоторым объектам реального мира или знакомым программисту абстракциям (например, матриц) из предметной области. Поэтому всё сказанное справедливо не только для DDD, но и для любого дизайна в ООП стиле. Придумывать синтетическую абстракцию поверх доменной модели, которая создаётся естественно и интуитивно при таком проектировании, зачастую значит идти против себя и против тех, кто будет заниматься сопровождением кода.
12 ...
18

Information

Rating
Does not participate
Location
Россия
Date of birth
Registered
Activity