Pull to refresh
105.25
Productivity Inside
Для старательного нет ничего невозможного

Как писать код, который будут использовать повторно

Reading time 6 min
Views 4K
Original author: Ravi Shankar Rajan


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

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

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

Почему так получается?

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

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

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

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

Вот несколько практических рекомендаций относительно того, как сделать код пригодным для многократного применения.

Избегайте дублирования


Как справедливо сказал Лемони Синкет: «Не повторяйтесь. Во-первых, вы повторяетесь, во-вторых, говорите одно и то же, а в-третьих, это все уже слышали».

Помните, мы говорили о повторном использовании, которое происходит спонтанно? Вот именно этим (и только этим) должно ограничиваться назначение эргономичного кода. Так что пишите код, который вам нужен в данный конкретный момент, и продолжайте в том же духе, пока не заметите, что вам приходится раз за разом решать одну и ту же проблему. Тогда проведите рефакторинг, поместите код в какое-нибудь доступное местоположение и обращайтесь к нему по мере неоходимости. Действуя таким образом, вы получите на выходе не расточительный обобщенный код, а код, в котором нет дублирования.

По сути, то же самое постулирует и принцип DRY (Don’t repeat yourself – Не повторяйтесь), в котором говорится, что в коде не должна многократно прописываться одна и та же логика – это порождает технический долг. Бессмысленные повторы забивают систему, снижают качество кодовой базы и вдобавок превращают ее в ночной кошмар для тех, кому выпадет на долю поддерживать код продукта. Важно помнить: принцип DRY – это своего рода философия, которая призывает отказаться от личных программистских амбиций и делать так, как будет лучше для проекта. Если кто-то уже что-то сделал – пользуйтесь. Не надо изобретать велосипед.

Убедитесь, что класс/метод/функция отвечает за что-то одно


По этому поводу можно вспомнить красивое высказывание Луи Салливана: «Форма происходит от назначения». В переводе это означает следующее: если функция, класс или метод делают одну-единственную вещь, то и изменять их вы будете только по одной причине. Да, при таком подходе зачастую придется создавать методы, которые будут использовать другие методы, но зато они будут оставаться простыми и не слишком сцепленными.

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

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

  • Они должны быть небольшими
  • Они должны делать что-то одно, причем хорошо

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

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

Не злоупотребляйте наследованием


Иногда мы, разработчики, пытаемся заглянуть в далекое будущее проекта и начинаем вбрасывать дополнительные функции с мыслями «а вдруг понадобится» и «когда-нибудь да пригодится». Не надо так. До сих пор вам это не пригождалось, сейчас вам это не нужно и в большинстве случаев… вам это не понадобится (перефразируя известный принцип YAGNI – You Ain’t Gonna Need It). Данный принцип применим и к наследованию. Не вводите его, если у вас нет полной уверенности, что реализация будет повторяться многократно.

При всем при этом наследственность – отличный способ добавить классу функциональности. Но программистам свойственно хватать через край, выстраивая иерархии классов в шесть и больше уровней. «Банда четырех» в своей книге «Паттерны проектирования» ёмко обрисовывает риски чрезмерного наследования:

«Поскольку подклассу доступны детали реализации родительского класса, то часто говорят, что наследование нарушает инкапсуляцию».

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

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

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

В заключение


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

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

Всегда помните: повторное использование начинается с того, что вы нашли удачное решение для той или иной задачи. Все остальное должно проистекать из этой предпосылки, и вы вправе выбрать тот метод, который наиболее уместен для того, чтобы вывести определенный фрагмент кода на новый уровень. Как справедливо заметил Ральф Джонсон: «Прежде чем думать о повторном использовании кода, нужно позаботиться о том, чтобы им вообще можно было пользоваться».
Tags:
Hubs:
+4
Comments 3
Comments Comments 3

Articles

Information

Website
productivityinside.com
Registered
Founded
Employees
101–200 employees
Location
Россия