Do not repeat yourself

Do not repeat yourself

Принцип программирования DRY — don’t repeat yourself / не повторяйте себя

Следование принципу программирования «DRY» позволяет добиться высокой сопровождаемости проекта, простоты внесения изменений и качественного тестирования.

Если код не дублируется, то для изменения логики достаточно внесения исправлений всего в одном месте и проще тестировать одну (пусть и более сложную) функцию, а не набор из десятков однотипных. Следование принципу DRY всегда приводит к декомпозиции сложных алгоритмов на простые функции. А декомпозиция сложных операций на более простые (и повторно используемые) значительно упрощает понимание программного кода. Повторное использование функций, вынесенных из сложных алгоритмов, позволяет сократить время разработки и тестирования новой функциональности.

Следование принципу DRY приводит к модульной архитектуре приложения и к чёткому разделению ответственности за бизнес-логику между программными классами. А это — залог сопровождаемой архитектуры. Хотя чаще не DRY приводит к модульности, а уже модульность, в свою очередь, обеспечивает принципиальную возможность соблюдения этого принципа в больших проектах.

В рамках одного программного класса (или модуля) следовать DRY и не повторяться обычно достаточно просто. Также не требует титанических усилий делать это в рамках небольших проектов, где все разработчики «владеют» всем кодом системы. А вот в больших проектах ситуация с DRY несколько сложнее — повторы чаще всего появляются из-за отсутствия у разработчиков целостной картины или несогласованности действий в рамках команды. Следовать принципу «don’t repeat yourself» в рамках больших проектов не так просто, как это может показаться на первый взгляд. От разработчиков требуется тщательное планирование архитектуры, а от архитектора или тимлида требуется наличие видения системы в целом и чёткая постановка задач разработчикам.

В пректировании DRY тоже имеет место — доступ к конкретному функционалу должен быть доступен в одном месте, унифицирован и сгруппирован по какому-либо принципу, а не «разбросан» по системе в произвольных вариациях. Этот подход пересекается с принципом единственной ответственности из пяти принципов SOLID, сформулированных Робертом Мартином.

DRY — это один из ключевых принципов создания хороших приложений, унификация и повторное использование повышает качество кодовой базы и улучшает сопровождаемость.

Так ли хорош DRY или все же он может нарушать O из SOLID

Принцип DRY (Do not Repeat Yourself) давно всем вполне очевиден и любим многими программистами. И многие согласны, что Copy/Paste это совсем не круто. В этой статье я хочу привести пример того, в каких случаях в промышленном программировании использование Copy/Paste более уместно и помогает красиво реализовать Open-Closed принцип из SOLID.

Напомню, что Open-closed принцип призывает программистов проектировать классы таким образом, чтобы они были открыты для расширения, но при этом закрыты для модификации. Модифицировать класс разрешается только в том случае, если в классе обнаружена ошибка. Если же требуется добавить функциональность, то принцип призывает создать новый класс и воспользоваться либо наследованием, либо реализацией того же самого интерфейса.

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

Имеем класс Parcel, который описывает работу обычной посылки:

Очень соблазнительно просто добавить поле и в старый класс Parcel, и в интерфейс IParcel:

Однако, такой код не должен проходить CodeReview! Строгий и опытный «проверяльщик» кода должен вернуть его с замечанием: «такая реализация нарушает Open-closed принцип.»

Гораздо лучше создать новый класс UrgentParcel, и не нужно будет менять ни интерфейс, ни класс Parcel. Файлы класса и интерфейса останутся нетронутыми:

Это будет соблюдением Open-closed принципа, и такой код не получит замечания при CodeReview.

Теперь давайте вернемся к DRY и к тому, каким образом он мешает реализовать Open-closed принцип.

Представим, что в классе Parcel у нас есть поле «статус посылки» и некая логика изменения этого статуса:

Нужно ли эту логику копировать в класс UrgentParcel? Принцип DRY говорит, что ни в коем случае. Гораздо лучше, чтобы класс UrgentParcel просто наследовался от класса Parcel, что решит задачу и не придется Copy/Paste’ть тело метода ArrivedToRecipient в класс UrgentParcel.

Однако, если не копировать код, а наследовать его, то изменения метода ArrivedToRecipient в классе Parcel сразу же приведут к изменению поведения класса UrgentParcel, что и будет являться нарушением Open-closed принципа. Это действительно нарушение, потому что задача со срочными посылками (реализация класса UrgentParcel) уже была сдана, протестирована и, как следствие, работает «в бою». Значит, эта логика, реализованная в методе UrgentParcel.ArrivedToRecipient и примененная к срочным посылкам — всех устраивает и она НЕ должна меняться при изменении работы других видов посылок. Так вот Open-closed принцип как раз и предназначен защищать систему от подобных действий неопытных junior-программистов, которые, решая задачу, как обычно «в лоб», не осознают еще пока всех зависимостей, и их изменения в одном месте затрагивают многие другие функциональные области.

Обычно, одним из главных аргументов в пользу DRY является тот факт, что, если найдена ошибка в методе ArrivedToRecipient, то ее надо исправлять везде, куда ее скопировали. Так вот этого, как раз, делать не нужно. Если найдена ошибка в методе ArrivedToRecipient при работе с обычными посылками, то и исправлять надо именно работу обычных посылок. А на работу срочных посылок никто не жаловался и, вероятно, всех устраивает, как срочные посылки работают.

Для перфекционистов, к коим я себя тоже причисляю, я бы предложил оставить комментарий, который позволил бы не забыть про все те места, куда копировался метод, и помочь поднять вопрос: а правильно ли у нас работает этот метод для срочных посылок.

Вот пример такого комментария:

Очень интересно мнение сообщества по этому вопросу. Заранее благодарен за ваши комментарии.

Ошибочное понимание принципа DRY

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Я знаю, о чём вы подумали: «Ещё одна скучная статья про DRY? Нам их мало, что ли?».

Возможно, вы правы. Но я встречаю слишком много разработчиков (junior и senior), применяющих DRY так, словно они охотятся на ведьм. Либо совершенно непредсказуемо, либо везде, где можно. Так что, судя по всему, в интернете никогда не будет достаточно статей о принципе DRY.

Если кто не знает: принцип DRY — это «Don’t Repeat Yourself» (не повторяйся), и впервые о нём упомянуто в книге The Pragmatic Programmer. Хотя сам по себе этот принцип был известен и применялся задолго до появления книги, однако в ней ему было дано название и точное определение.

Итак, без лишних рассуждений отправимся в путешествие по чудесной стране DRY!

Не повторяйте свои знания

Хотя фраза «не повторяйся» звучит очень просто, в то же время она слишком общая.

В книге The Pragmatic Programmer принцип DRY определяется как «Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы».

Всё замечательно, но… что такое «часть знания»?

Я бы определил это как любую часть предметной области бизнеса или алгоритма.

Если взять популярные аналогии из электронной коммерции, то класс shipment и его поведение будут частью предметной области бизнеса вашего приложения. Отгрузка — это реальный процесс, используемый вашей компанией для отправки товара своим клиентам. Это часть бизнес-модели компании.

Следовательно, логика работы класса shipment должна появляться в приложении однократно.

Причина очевидна: допустим, вам нужно отправить груз на склад. Для этого придётся активировать логику отгрузки в 76 разных местах приложения. Без проблем: вставляем логику 76 раз. Через некоторое время приходит начальник и просит кое-что изменить в логике: вместо одного склада компания теперь распределяет товары по трём складам. В результате вы потратите кучу времени на изменение логики, поскольку вам придётся сделать это в 76 местах! Совершенно впустую потраченное время, хороший источник багов и прекрасный способ выбесить начальство.

Ещё один пример: допустим, вы пишете хитрый класс для парсинга В-деревьев. Его тоже можно считать знанием: это алгоритм, который должен быть определён однократно. А уже представление знания можно использовать где вашей душе угодно без повторения самого знания.

DRY и дублирование кода

Так значит DRY относится к knowledge? К бизнес-логике?

Начнём с очевидного:

Этот код выглядит недурно, правда?

Однако Лёха, твой коллега-разработчик, с вами не согласится. Когда Лёха увидит этот код, подойдёт к вашему столу и возмутится:

Лёха — начинающий эксперт в вашей компании, и не имеет понятия об ООП.

Я прямо вижу, как вы, замечательный разработчик, смотрите на Лёху, словно опытный садовник на личинку, и отвечаете:

Здесь никак не нарушается принцип DRY. Лёха затыкается, подавленный вашей аурой величия, заполняющей комнату. Но вы поставили под сомнение его знания, и он злится. Лёха гуглит приницп DRY, находит другой пример вашего кода, возвращается и тычет вам в лицо:

Лёха торжествующе восклицает: «Ты олень! Этот код не соответствует DRY!». А вы, прочитав эту статью, отвечаете: «Но бизнес-логика и знание всё ещё не повторяются!»

Вы снова правы. Метод проверяет результат парсинга CSV только в одном месте ( validateProduct() ). Это знание, и оно не повторяется.

Вы молниеносно набираете код:

Выглядит лучше, верно?

Но Лёха ещё не убеждён. Со спокойствием высшего духовного наставника, вы ставите точку в споре: «Многие думают, что DRY запрещает дублировать код. Но это не так. Суть DRY гораздо глубже».

Кто это сказал? Дэйв Томас, один из авторов The Pragmatic Programmer, книги, в которой дано определение самого принципа DRY!

Применяем DRY везде: рецепт катастрофы

Бесполезные абстракции

Возьмём более жизненный, интересный пример. Сейчас я работаю над приложением для компании, занимающейся производством фильмов. В него можно легко загружать сами фильмы и метаданные (заголовки, описания, титры…). Всё эта информация затем отображается в VoD-платформе. Приложение построено на основе паттерна MVC, и выглядит оно так:

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Но создавать с помощью этого приложения контент могут не только съёмочные группы. К примеру, им может воспользоваться и команда по управлению контентом в моей компании. Почему? Некоторые съёмочные группы не хотят этим заниматься. У съёмочной команды и команда по управлению контентом совершенно разные потребности. Вторые работают с CMS, а первые — нет.

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

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Выглядит как очевидное и уродливенькое нарушение принципа DRY: повторяются представления (view) и контроллер.

Более того, контроллеры не должны содержать в себе бизнес-логику. Если вспомните определение DRY, то это как раз знание, которое не стоит дублировать.

Иными словами, если пытаться везде применять DRY, то можно прийти к одному из двух:

Очевидно, что ни то, ни другое вам не улыбается.

Преждевременная оптимизация

Не стоит применять DRY, если ваша бизнес-логика ещё не содержит никаких дублирований. Конечно, всё зависит от ситуации, но, как показывает практика, если применять DRY к чему-то, что используется только один раз, то можно оказаться в ситуации преждевременной оптимизации.

Если вы начинаете что-то абстрагировать, потому что «позже это может понадобиться», то не делайте этого. Почему?

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

Сначала напишите код, отладьте его, а затем уже держите в уме все эти принципы (DRY, SOLID и прочие), чтобы эффективно рефакторить код. Работать с нарушением принципа DRY нужно тогда, когда знание уже дублировано.

Дублирование знания?

Помните, выше я говорил, что повторение бизнес-логики всегда является нарушением DRY? Очевидно, что это справедливо для ситуаций, когда повторяется одна и та же бизнес-логика.

Вы уже слышите, как ваш коллега Лёха нежно вопит вам в ухо: «Это очевидное нарушение всего, во что я верю! А как же принцип DRY? У меня сердце сжимается!».

Но Лёха снова ошибается. С точки зрения электронной коммерции, время доставки товара покупателю ( Shipment::calculateDeliveryDay() ) не имеет отношения к сроку возврата товара покупателем ( Return::calculateLastReturnDay ).

Это две разные функциональности. То, что выглядит дублированием кода, на самом деле чистое совпадение. Что произойдёт, если вы объедините два метода в один? Если ваша компания решит, что покупатель теперь может вернуть товар в течение месяца, то вам снова придётся разделить метод. Ведь если этого не сделать, то срок доставки товара тоже будет составлять один месяц!

А это не лучший способ завоевать лояльность клиентов.

DRY — не просто принцип для педантов

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself
Сегодня даже джин может быть DRY!

DRY — это не то, что вам стоит уважать только в своём коде. Не нужно повторять знания в каждом аспекте вашего проекта. Снова процитирую Дэйва Томаса: «Понятие «знания» в системе охватывает далеко не только код. Оно относится и к схемам баз данных, планам тестирования, системе сборки и даже документации».

Суть DRY проста: не нужно обновлять параллельно несколько вещей, если вносится какое-то изменение.

Если ваше знание дважды повторяется в коде, и вы забыли обновить одно представление, то возникнут баги. Это приведёт к заблуждениям и недоразумениям в документации, и к совершенно ошибочной реализации. И так далее, и тому подобное.

DRY — это принцип

В начале своей карьеры я часто страдал от аналитического паралича. Все эти принципы не давали мне быть продуктивным и эффективным. Было слишком сложно, и я не хотел всё испортить.

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

Однако принципы — не правила. Они лишь инструменты, помогающие идти в правильном направлении.

В разработке у всего есть цена. И DRY не исключение. Очевидно, что не стоит везде повторять бизнес-логику, но также не нужно и тесно объединять всё подряд, экономя на абстракциях. Необходимо искать баланс, который зависит от текущей ситуации.

Принципы для разработки: KISS, DRY, YAGNI, BDUF, SOLID, APO и бритва Оккама

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Хорошему программисту необходимо уметь совмещать свои навыки со здравым смыслом. Все дело в прагматизме и навыке выбора лучшего решения для вашей проблемы. Когда вы сталкиваетесь с проблемой при разработке ПО, вы можете воспользоваться базовыми принципами, которые помогут в выборе самого правильного подхода.

Последовательное применение этих принципов упростит ваш переход от миддла к сеньору. Вы можете обнаружить, что некоторые (вероятно) вы применяете интуитивно.

Принципов много. Мы остановимся на семи самых важных. Их использование поможет вам в развитии и позволит стать лучшим программистом.

1. YAGNI

You Aren’t Gonna Need It / Вам это не понадобится

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

Этот принцип применим при рефакторинге. Если вы занимаетесь рефакторингом метода, класса или файла, не бойтесь удалять лишние методы. Даже если раньше они были полезны – теперь они не нужны.

Может наступить день, когда они снова понадобятся – тогда вы сможете воспользоваться git-репозиторием, чтобы воскресить их из мертвых.

2. DRY

Don’t Repeat Yourself / Не повторяйтесь

Эта концепция была впервые сформулирована в книге Энди Ханта и Дэйва Томаса «Программист-прагматик: путь от подмастерья к мастеру».

Идея вращается вокруг единого источника правды (single source of truth — SSOT). Что это вообще такое?

В проектировании и теории информационных систем единый источник истины (SSOT) – это практика структурирования информационных моделей и схемы данных, которая подразумевает, что все фрагменты данных обрабатываются (или редактируются) только в одном месте… SSOT предоставляют достоверные, актуальные и пригодные к использованию данные.

Использование SSOT позволит создать более прочную и понятную кодовую базу.

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

В большинстве случаев дублирование кода происходит из-за незнания системы. Прежде чем что-либо писать, проявите прагматизм: осмотритесь. Возможно, эта функция где-то реализована. Возможно, эта бизнес-логика существует в другом месте. Повторное использование кода – всегда разумное решение.

3. KISS

Keep It Simple, Stupid / Будь проще

Этот принцип был разработан ВМС США в 1960 году. Этот принцип гласит, что простые системы будут работать лучше и надежнее.

У этого принципа много общего с переизобретением колеса, которым занимались в 1970-х. Тогда он звучал как деловая и рекламная метафора.

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

Иногда самое разумное решение оказывается и самым простым. Написание производительного, эффективного и простого кода – это прекрасно.

Одна из самых распространенных ошибок нашего времени – использование новых инструментов исключительно из-за того, что они блестят. Разработчиков следует мотивировать использовать новейшие технологии не потому, что они новые, а потому что они подходят для работы.

4. Big Design Up Front

Глобальное проектирование прежде всего

Этот подход к разработке программного обеспечения очень важен, и его часто игнорируют. Прежде чем переходить к реализации, убедитесь, что все хорошо продумано.

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

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

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

Очень распространенный контраргумент заключается в том, что стоимость решения проблем зачастую ниже стоимости времени планирования. Чем с меньшим количеством ошибок столкнется пользователь, тем лучше будет его опыт. У вас может не быть другого шанса справиться с этими ошибками.

5. SOLID

Это наиболее известный принцип разработки ПО. Solid — это аббревиатура от:

S) Single-responsibility principle /Принцип единственной ответственности

Его важность невозможно переоценить. Каждый объект, класс и метод должны отвечать только за что-то одно. Если ваш объект/класс/метод делает слишком много, вы получите спагетти-код. Вот пример:

Этот метод кажется безобидным, но на самом деле он делает слишком много:

O) Open–closed principle / Принцип открытости-закрытости

Программные объекты должны быть открыты для расширения, но закрыты для модификации. Речь о том, что нельзя переопределять методы или классы, просто добавляя дополнительные функции по мере необходимости.

Хороший способ решения этой проблемы – использование наследования. В JavaScript эта проблема решается с помощью композиции.

Простое правило: если вы изменяете сущность, чтобы сделать ее расширяемой, вы впервые нарушили этот принцип.

L) Liskov substitution principle / Принцип подстановки Лисков

Этот принцип гласит, что объекты старших классов должны быть заменимы объектами подклассов, и приложение при такой замене должно работать так, как ожидается.

I) Interface segregation principle / Принцип разделения интерфейсов

Этот принцип был сформулирован Робертом Мартином, когда он консультировал Xerox, и он очевиден.

Объекты не должны зависеть от интерфейсов, которые они не используют

ПО должно разделяться на независимые части. Побочные эффекты необходимо сводить к минимуму, чтобы обеспечивать независимость.

Убедитесь, что вы не заставляете объекты реализовывать методы, которые им никогда не понадобятся. Вот пример:

Не все животные могут fly, walk или swim, поэтому эти методы не должны быть частью интерфейса или должны быть необязательными.

D) Dependency inversion principle / Принцип инверсии зависимостей

Этот принцип невозможно переоценить. Мы должны полагаться на абстракции, а не на конкретные реализации. Компоненты ПО должны иметь низкую связность и высокую согласованность.

Заботиться нужно не о том, как что-то устроено, а о том, как оно работает. Простой пример – использование дат в JavaScript. Вы можете написать для них свой слой абстракции. Тогда если у вас сменится источник получения дат, вам нужно будет внести изменения в одном месте, а не в тысяче.

Иногда добавление этого уровня абстракции требует усилий, но в конечном итоге они окупаются.

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

6. Avoid Premature Optimization

Избегайте преждевременной оптимизации

Эта практика побуждает разработчиков оптимизировать код до того, как необходимость этой оптимизации будет доказана. Думаю, что если вы следуете KISS или YAGNI, вы не попадетесь на этот крючок.

Поймите правильно, предвидеть, что произойдет что-то плохое – это хорошо. Но прежде чем вы погрузитесь в детали реализации, убедитесь, что эти оптимизации действительно полезны.

Очень простой пример – масштабирование. Вы не станете покупать 40 серверов из предположения, что ваше новое приложение станет очень популярным. Вы будете добавлять серверы по мере необходимости.

Преждевременная оптимизация может привести к задержкам в коде и, следовательно, увеличит затраты времени на вывод функций на рынок.

Многие считают преждевременную оптимизацию корнем всех зол.

7. Бритва Оккама

Бри́тва О́ккама (иногда ле́звие О́ккама) — методологический принцип, в кратком виде гласящий: «Не следует множить сущее без необходимости» (либо «Не следует привлекать новые сущности без крайней на то необходимости»).

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

Заключение

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

Использование базовых, но действенных принципов позволит вам стать лучшим программистом и иметь более четкое представление о том, почему вы что-то делаете.

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

НПП ИТЭЛМА всегда рада молодым специалистам, выпускникам автомобильных, технических вузов, а также физико-математических факультетов любых других высших учебных заведений.

У вас будет возможность разрабатывать софт разного уровня, тестировать, запускать в производство и видеть в действии готовые автомобильные изделия, к созданию которых вы приложили руку.

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

Если вам интересно попробовать свои силы в решении тех задач, которые у нас есть, пишите в личку.

Мы большая компания-разработчик automotive компонентов. В компании трудится около 2500 сотрудников, в том числе 650 инженеров.

Мы, пожалуй, самый сильный в России центр компетенций по разработке автомобильной электроники. Сейчас активно растем и открыли много вакансий (порядка 30, в том числе в регионах), таких как инженер-программист, инженер-конструктор, ведущий инженер-разработчик (DSP-программист) и др.

У нас много интересных задач от автопроизводителей и концернов, двигающих индустрию. Если хотите расти, как специалист, и учиться у лучших, будем рады видеть вас в нашей команде. Также мы готовы делиться экспертизой, самым важным что происходит в automotive. Задавайте нам любые вопросы, ответим, пообсуждаем.

Три ключевых принципа ПО, которые вы должны понимать

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself
Разрабатывая приложения, мы постоянно сталкиваемся с новыми подходами, языками и концептами. И постоянно мы мечемся в сомнениях «смогу ли я быть на волне, оставаться конкурентоспособным, учитывая все изменения и тренды?». Давайте задумаемся на мгновение, вспомнив фразу из моего любимого фильма «Касабланка» — в любви законов новых нет — так создан свет.

Все, что касается любви, применимо и к коду. Новых законов в коде нет. Если вы четко понимаете основные идеи разработки, вы способны максимально быстро адаптироваться к новым подходам. В этой статье я расскажу вам о трех основных принципах, которые, наряду с другими, позволяют регулировать сложность разработки. Я поделюсь своим видением вопроса, которое, надеюсь, поможет вам в повседневной работе.

DRY – Don’t repeat yourself (не повторяй себя)

Этот принцип настолько важен, что не требует повторения! Обычно его упоминают акронимом DRY, который впервые появился в небезызвестной книге «The pragmatic programmer», но концепт, сам по себе, был известен довольно давно. Он относится к самым мелким частям вашего ПО.

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

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Самый простой подход по уменьшению сложности — разделить систему на управляемые части.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

К примеру, частью любой CMS, является компонент, отвечающий за управление пользователями. Этот компонент может быть разделен на другие компоненты, допустим, управление уровнями доступа, который сможет работать с другими компонентами системы безопасности.

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

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Принцип DRY требует, чтобы такие части информации встречались в вашем коде один, и только один раз.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Эти части должны иметь одно представление.

Обратите внимание на различие между информацией (данными), и ее представлением. Когда мы организовываем соединение с БД в нашей CMS, у нас будет код, который будет поднимать нужный драйвер, принимать авторизацию, и сохранять указатель на соединение в переменную. Здесь, код является информацией, описывающей, как мы получаем что-то (соединение). Переменная с указателем на соединение является представлением этой информации, которая может быть использована в любом месте системы. Если вдруг у нас изменится авторизация, нам нужно будет изменить только «данные», но не их представление.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Каждая часть данных должна иметь четкое, надежное представление в системе.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

В идеальной системе каждая часть бизнес логики инкапсулирует свои данные в представление – переменную или свойство класса. Эта переменная инкапсулирована в класс, отвечающий за часть архитектуры. Класс является частью компонента, который является реализацией части функционала системы.

Таким образом мы можем добраться до верха системы – сложного набора реализаций функционала. Такой подход организации называют модульной архитектурой, и, в свою очередь, DRY, является ее важной частью.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Задача архитектуры – управлять сложностью
Перестаем повторяться

Путей ликвидировать повторения довольно много. Hunt и Thomas предлагают генераторы кода и трансформацию данных. Но DRY, в итоге, является философией выдачи логики через представления.
Каждая часть вашего приложения может быть представлением, каждая часть выдает определенную логику – модуль управления авторизацией дает доступ вашим пользователям, класс пользователя содержит информацию про текущего пользователя с набором его свойств. Этот класс, в свою очередь, получает данные через представление БД.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

DRY — это философия, разбивающая логику на представления.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

DRY, и модульная архитектура требуют хорошего планирования. Для правильной организации иерархии представлений разделите вашу систему на небольшие части, которые смогут работать вместе. Если вам предстоит управление большим проектом, стоит рассмотреть идею его организации компонентами, с применением DRY. Постарайтесь следовать вот этим правилам:

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Если вдруг вы ловите себя на том, что этот код вы уже писали/встречали раньше, остановитесь, подумайте, и не повторяйте себя.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

В реальных проектах достичь 100% DRY практически нереально. Но, в свою очередь, проектов, которые не-DRY на неприемлемом уровне, и которыми сложно управлять, довольно много. И, возможно это для вас будет сюрпризом – 50% всех наших проектов провальны, если взглянуть на их код.

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

Пример

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

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Редко плохой код пишут плохие программисты.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Мы помним – повторяемость ликвидируется хорошим планированием. Срочные изменения в системе влекут срочные, неоптимальные решения в коде. Как только код подвергается плохим решениям, весь принцип DRY для данного решения перестает работать, до будущих изменений.

Если вы посмотрите на историю самых успешных IT компаний, многие из них были созданы людьми с пониманием проблемы — Bill Gates, Mark Zuckerberg, Steve Wozniak, Steve Jobs, Larry Page, Sergey Brin, Larry Ellison – эти люди знали, что им предстоит преодолеть для решения той или иной задачи. Но есть и компании, которые передают системное управление в руки бухгалтеров, а концептуальное – в руки консультантов. Ни те, ни другие не способны управлять такими областями.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

DRY достигается совместным планированием.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Именно поэтому многие решения работают только в Powerpoint, Photoshop, и 27” экранах. Это отчасти работало в, более или менее, статичных, сайтах. Но не сегодня, в мире, где куча разных интерактивных приложений, и устройств.

И программисты, стоящие последними в цепочке, обязаны оперативно исправлять ошибки в системе. Если они приправлены бухгалтерами, которые не в силах противостоять ежеминутным прихотям заказчика – все планирование летит к чертям, и пишется плохой, очень плохой код. Код перестает быть DRY.

Этот пример немного пессимистичен (хотя я довольно часто с ним сталкиваюсь), но он четко показывает, что DRY является концептом, зависящим от многих людей. Если вдруг вы работаете в компании, которая настолько же запущена, советую вам предложить изменения в процессах (к примеру – техническую оценку на ранних стадиях проектов).

Если вас просят не совать нос куда не нужно, читайте дальше – принцип YAGNI спасет вас!

KISS – keep it simple stupid (делайте вещи проще)

В конце 19-го века, физики пытались найти объяснение, как работают гравитация, магнитные поля, и оптика на больших расстояниях – расстояниях в пределах нашей солнечной системы. Была выведена теория о существовании некого эфира, который действует на все те силы, и он не может быть объяснен. Позже эта теория была расширена некими экспериментами, но ни один из них не объяснял проблемы.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Зачастую самое простое объяснение является самым правильным решением.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Чуть позже, Альберт Эйнштейн, сотрудник швейцарского патентного бюро, предложил отказаться от этой теории, пренебречь всеми расчетами расстояний, и просто считать, что время не является константой – оно относительно. Такое решение проблемы с учетом минимального количества зависимостей объяснимо принципом «бритвы Оккама».

И похожие концепты встречаются во многих областях. В разработке ПО (и некоторых других) мы называем его KISS. У этого акронима много значений, но все они сводятся к одному – вы должны стараться делать вещи как можно проще.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Большая часть прогресса в истории человечества была достигнута благодаря неординарным мыслителям.

Протокол HTTP очень часто приводится, как пример идеального, простого решения – изначально созданный для передачи гипертекстовых документов, сегодня он является основой для многих интерактивных приложений. Возможно, нам иногда приходится искать обходные решения проблем, связанных с ограничениями это протокола, и может быть в будущем его сменит другой протокол. Но на сегодня факт остается фактом: основываясь не нескольких методах запросов (GET/POST), кодах статусов и простых текстовых аргументах, HTTP является достаточно гибким и надежным. Именно поэтому веб-разработчики стараются выжимать из него максимум, и именно поэтому этот протокол все еще в строю.

Подход делать вещи проще довольно очевидный, но история разработки ПО полна различных плохих, сырых решений. Их еще часто называют отдельным словом – bloatware, или DOA (dead on arrival) — мертвое при рождении. Относительного такого софта можно применить теорию, похожую на теорию не-DRY кода… Тем не менее, успех интернета можно объяснить простыми, эффективными решениями.

Так как достичь максимально возможного, простого решения? Все сводится к возможности поддержки, и детализации в разработке ПО. Именно поэтому KISS следует применять на этапе определения требований. Стараясь реализовать требования клиента в вашем коде, попытайтесь выделить следующее:

И мы решили внедрить импорт из формата CSV. Решение заняло несколько строк кода, не было перегружено данными (если сравнивать форматы CSV и Excel), легко управлялось и поддерживалось. Excel запросто может экспортировать данные в формате CSV (как и многие другие программы, которыми клиент мог воспользоваться в будущем). И, учитывая минимальные затраты на реализацию этого требования, данное решение является отличным примером KISS.

Мораль – старайтесь рассмотреть вещи с простой стороны, если они выглядят сложными. Если клиент вам рассказывает свои требования, реализация которых вам кажется сложной, вы правы в любом случае. Даже учитывая, что некоторые вещи действительно сложны в реализации, мы нередко сталкиваемся с решениями, которые перегружены необоснованно. Это случается, т.к. в процесс разработки вовлечены некоторые люди, не имеющие технического опыта для правильного расчета затраты/выгода. И они просто не видят всей проблемы. Поэтому всегда дважды проверяйте требования клиента, и убедитесь, что это именно то, что ему нужно. Обсудите критические моменты, объясните ему, почему другие решения могут подойти лучше.

You ain’t gonna need it – вам это не понадобится

Когда Google запустил Google+, Mark Zuckerberg, основатель Facebook-а, был одним из первых зарегистрированных в социалке, которая была призвана превзойти его сеть. Он написал всего лишь одну строку в разделе “Обо мне” – «Я строю вещи». Лично я считаю это блестящим предложением, раскрывающим всю суть программирования. Почему вы выбрали путь кодера? Энтузиазм в технических решениях? Красота эффективности? Чтобы вы не ответили, скорее всего это не будет «построить 100500-ый корпоративный сайт со стандартным функционалом». И тем не менее, многие из нас зарабатывают именно этим. Где бы вы не работали, вам периодически приходится сталкиваться со скучными, рутинными задачами.

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Я программирую. Я строю вещи.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Принцип «Вам это не понадобится» (YAGNI – you ain’t gonna need it) как раз призван решать такие задачи.

Это значит, что то, что не задумано в системе, не должно появляться в коде. К примеру, достаточно часто доступ к БД осуществляется через абстракцию, которая может иметь реализацию для разных драйверов – MySQL, PostgreSQL, Oracle. Если вы работаете над сайтом, который размещается на LAMP стеке – какова вероятность того, что клиент сменит БД? Не забывайте, что концепт всегда пишется под бюджет – верно?

Если в бюджете не предусмотрена абстракция для БД – этой абстракции не должно быть в системе. Если вдруг клиенту понадобится переехать на другую БД, довольно очевидно, что это повлечет затраты на изменение системы.

Вы, должно быть, заметили разницу между YAGNI и DRY системами. Последняя призвана уменьшать сложность, разделяя систему на управляемые компоненты, в то время, как первая уменьшает сложность, уменьшая количество этих компонент. Принцип YAGNI похож на KISS – он старается делать вещи как можно проще. Но KISS старается искать простые решения, а YAGNI просто не делает никаких решений!

Теодор Старджон, американский фантаст, выдвинул закон – «90% всего – полная чушь». Довольно радикальное утверждение, и не всегда применимое в реальных проектах. Не забывайте, что «чушь» может отнимать уйму времени. Существует негласное правило: примерно 80% затраченного времени на разработку тратится на реализацию всего лишь 20% функционала системы. Вспомните ваши проекты. Каждый раз, когда я пересматриваю свои, я постоянно убеждаюсь, как точно работает правило 80/20.
Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

80% затраченного времени на разработку тратится на реализацию всего лишь 20% функционала системы.


Эта стратегия очень хорошо применима в компании, в которой принято соблюдать дедлайны в перемешку с не очень четкими концептами. Никто не оценит вашу абстракцию БД. Даже есть шанс, что ваш начальник понятия не имеет, что такое абстракция БД.

Тем не менее, даже если этот подход может казаться довольно простым, зачастую не так то и просто отделить нужные от ненужных частей. К примеру, даже если у вас реализована абстракция, это не даст вам никакого выигрыша при дампе БД. Ключевым моментом является то, как мы смотрим на ПО – нас учили писать код, который легко поддерживать, и который будет выгоден в будущем. Т.е. нас учили смотреть наперед, учитывая все возможные изменения. Иногда это критично для больших проектов, но никак не для маленьких. Перестаньте думать о будущем! Если вдруг небольшому сайту потребуются радикальные изменения, будет лучше начать все с нуля. И это не такая уж и проблема, учитывая общие инвестиции.

Планирование проекта

Начиная планирование нового проекта, постарайтесь учесть следующее:

Каждый день мы внедряем новые возможности, которые, как нам кажется, будут полезны. Следовательно, мы загадываем наперед, и реализовываем слишком много. К примеру, многие клиенты хотят иметь мобильные версии своих сайтов. Мобильность может быть представлена многими значениями, это не обязательно дизайнерское решение. Это сценарий использования! Люди, которые пользуются мобильными сайтами, являются мобильными. Это значит, что им может понадобиться другая информация, или функционал, чем тем, которые пользуются десктопной версией. Представьте сайт кинотеатра – пользователи на пути в кинотеатр, в автобусе, скорее всего захотят увидеть время начала сеанса, а не 50-метровый трейлер.

С адекватным бюджетом, вы постараетесь сделать отдельный анализ требований к мобильной версии. Без такого анализа вы просто выдадите ту же информацию, что и для десктопа. И этого может быть достаточно для многих проектов. Потому что на сегодня, многие мобильные браузеры способны правильно настраивать вид сайта, и здесь можно применить радикальный подход YAGNI – не делать мобильной версии вообще!

Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Плохие концепты можно часто определить по отсутствию не-функциональных требований.Do not repeat yourself. Смотреть фото Do not repeat yourself. Смотреть картинку Do not repeat yourself. Картинка про Do not repeat yourself. Фото Do not repeat yourself

Не-функциональные требования не описывают поведения системы, они описывают дополнительные свойства, по которым можно оценить качество продукта. Т.к. описание качества предполагает знание продукта, плохие концепты можно часто определить по отсутствию не-функциональных требований. Возможность поддержки, уровень документации, легкость интеграции – все это примеры не-функциональных требований. Эти требования должны быть оценимыми. Т.е. «Сайт должен грузиться быстро» — слишком обобщенно, а вот «Сайт должен грузиться за 2 секунды в процессе теста производительности» — очень даже конкретно, и ясно. Если вы хотите применить подход YAGNI, постарайтесь учесть некоторые не-функциональные требования, даже если они не учтены в концепте (или учтены, но не очень четкие). Когда вы пишете такое требование, будьте реалистами – небольшой сайт, с 20-50 посещениями в день, не требует трехдневной настройки производительности, т.к. сайт будет грузиться быстро и так, если сервер не перегружен. Даже если компания сможет повысить свою посещаемость, купить более мощный хостинг не должно составить проблем.

Ну и напоследок, всегда помните правило 80/20. Старайтесь определить затратные по времени задачи. Если такая задача обязательна к реализации, придется ее сделать. Вопрос будет только как это сделать. Нужен ли нам еще один фреймворк с небольшим комьюнити? Придется ли вам переходить на только что вышедшую версию библиотеки, если документация по ней все еще не обновлена? Есть ли необходимость использовать новую CMS, если не все расширения к ней работоспособны? Насколько глубокий анализ придется провести, чтобы реализовать вашу задачу? Подход «так приходится делать всегда» — не очень захватывающий, но он поможет вам решить задачу без сюрпризов.

Также важно понимать, что все это вовсе не значит, что можно сесть и писать плохой код, приправленный хаками. Вы просто пишете небольшое приложение, а не плохое! Тем не менее, подход «вам это не понадобится» довольно практичен. Если он помогает сократить несколько строк кода, лично я считаю, что можно внести эту работу в бюджет, и небольшое не-DRY вполне приемлемо. Т.е. можно согласиться не немного возросшие затраты по поддержке – мы живем в реальном мире.

Давайте вернемся в нашей основной идее – мы строим вещи. Бетховен написал “Diabelli Variations” по контракту. Я не думаю, что он шел на компромиссы по бюджету. Он потратил больше времени, но не выпустил плохую музыку – он стремился написать идеальную.

Конечно, я не подразумеваю, что мы все гении, и что наша гениальность должна проявляться в каждой строчке кода, но мне нравится думать об архитектуре ПО, как о композиции. Я – страстный разработчик, потому что я хочу строить идеальные композиции, и хочу гордиться вещами, которые я строю.

Источники информации:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *