ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.11.2023

Просмотров: 897

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

355 связано с грязным кодом, касается по большей части простых разработ- чиков [11, 13]. На самом деле, разница между грязным кодом и неопти- мальным стратегическим решением, по сути, не такая уж и большая: при добавлении новой возможности в систему приходится расплачи- ваться за недальновидность в прошлом.
Во время кодирования, как и во время принятия любых других ре- шений, разработчик должен рассматривать краткосрочные и долгосроч- ные выгоды. Обычно в процессе развития приложения накапливается как груз крупных стратегических ошибок или непониманий требований, так и масса мелких тактических ошибок, типа длинных функций с не- очевидными обязанностями и сложными побочными эффектами; рас- плывчатых классов, с нечеткими границами и обязанностями; отсутст- вие идиом именования и документации и т.п.
И если команда не отдает свои долги путем переосмысливания су- ществующих решений и их последующего рефакторинга, если она не старается держать качество кода на высоком уровне, то рано или поздно это повысит стоимость развития и сопровождения кода настолько, что все существующие средства будут уходить на оплату процентов по тех- ническому долгу. Это, в свою очередь, приведет к низкой продуктивно- сти разработчиков, и взаимной неудовлетворенности команды разра- ботчиков и заказчика.
Существует несколько способов выплаты технического долга, и наи- более простым из них является рефакторинг системы или некоторых ее частей, выделение новых абстракций, написание юнит-тестов, коммен- тариев или полное переписывание некоторых кусков системы. Однако, как это часто бывает, вместо прагматичного подхода к улучшению не- которых частей системы для получения разумной выгоды в будущем, программист начинает переписывать все подряд: что нужно, что не обя- зательно и что вообще переписывать не стоит. Все это приводит к еще одной метафоре, которая как раз описывает подобное неустранимое желание к переписыванию старого кода – к синдрому рефакторинга
[28].
Первый симптом такого синдрома – неуважительное отношение к чужому коду. Надо сказать, что, во-первых, это не красит разработчика, а во-вторых, в большинстве случаев не уменьшает технический долг, который должен уменьшаться благодаря этому рефакторингу. Система в целом не становится более сопровождаемой и понятной, просто она те- перь ближе и понятнее одному конкретному человеку. Конечно, на не- которое время сопровождаемость системы улучшается, но все выгоды закончится ровно тогда, когда за этот кусок кода берется другой разра- ботчик. И процесс начинается сначала.
Стремление к идеальному коду (симптом 2) является наиболее бла- гим намерением при изменении существующего кода. Слепое следова- ние непродуманным стандартам кодирования не многим лучше, чем отсутствие стандартов кодирования вовсе. Красота кода – не самоцель; код может быть красиво оформленным, все функции могут быть не- большого размера с достаточным количеством комментариев и даже


356 покрыты юнит-тестами. Но это не значит, что этот код будет справлять- ся со своей основной задачей, поскольку, банально, никто не удосужил- ся узнать эту задачу у пользователя.
Для получения идеального результата требуется бесконечное коли- чество усилий. Код должен быть достаточно красив, достаточно поня- тен, с достаточным количеством комментариев и юнит-тестов. Их не должно быть много, и их не должно быть мало; их должно быть ровно столько, чтобы затраты на их написание и сопровождение были оправ- даны.
С течением времени, с высоты своего собственного профессиональ- ного опыта, благодаря более четкому пониманию требований пользова- теля и, пониманию своей собственной системы, даже свой код начинает выглядеть ужасным (симптом 3). Поэтому улучшение своего собствен- ного кода – процесс настолько же полезный, как и улучшение кода чу- жого. Но бывают случаи, когда даже свой код подвергается значитель- ным переделкам не раз за год, но при этом в нем толком ничего и не меняется.
Это нормально, если человек это делает для своего собственного проекта, когда при этом нарабатываются новые решения уже хорошо известных задач, но это не совсем разумно для коммерческого продукта.
Ведь получается, что код переписывается просто потому, что програм- мист узнал о новой возможности в языке программирования или о но- вой библиотеке, которая значительно лучше решает эту же задачу. Про- ходит время, появляются новые возможности, процесс опять повторяет- ся с начала, но при этом никакие долги не выплачиваются и ничего цен- ного в систему не добавляется.
Практически в любом деле прагматизм и отсутствие крайностей яв- ляется лучшим выбором, и рефакторинг существующего кода – не ис- ключение. Не стоит забывать о законе Парето – принципе 80/20. В соот- ветствии с ним двадцати процентов усилий на улучшение кода и под- держание его в адекватном состоянии достаточно, чтобы улучшить его на 80%. А если это не так, то инвестировать дополнительные средства на покрытие столь большого технического долга просто нет смысла, и стоит начать все с чистого листа.
И тут начинает проявляться эффект второй системы [38]. Когда тех- нический долг команды начинает превышать все мыслимые и немысли- мые границы, у команды разработчиков появляется как минимум два способа его погашения: провести рефакторинг системы таким образом, чтобы стоимость будущих изменений была не столь высокой или оста- вить текущую версию системы в покое и переписать все заново. В пер- вом случае легко столкнуться с синдромом рефакторинга, когда изме- нения делаются не с расчетом уменьшения стоимости будущих измене- ний, а вносятся просто ради изменений.
Во втором же случае может возникнуть «эффект второй системы», когда развиваются и совершенствуются уже никому не нужные функ- ции системы, а мысль «а не переписать ли все» является первой и един- ственной, которая приходит в голову команде, как только она сталкива-


357 ется с чужим кодом. И хотя в классическом понимании «эффект второй системы» немного отличается от патологической нелюбви к чужому коду и постоянному его переписыванию, оба эти случая имеют и что-то общее, так что имеет смысл оба эти симптома рассмотреть совместно.
Поскольку многие разработчики и архитекторы вкладывают свою душу при создании программных систем, совсем не удивительно, что приступая к новой версии своего детища, они стараются исправить все ошибки и недочеты, и пытаются создать идеальную систему. Кроме того, окрыленные успехом первой системы (в случае неудачи до второй версии системы дело просто не доходит), разработчики начинают ду- мать, что у них достаточно знаний и опыта в данной предметной облас- ти, чтобы на основе существующих частных знаний делать общие вы- воды. Это зачастую приводит к преждевременному обобщению реше- ния, что очень часто считается злом не меньшим, чем преждевременная оптимизация.
Другой проблемой при создании второй системы является непра- вильная переоценка ценностей, когда доводятся до совершенства мо- рально устаревшие функции системы, а принципиально новые подходы и решения не развиваются. Это приводит к идеальной реализации нико- му не нужных функций, что может быть и полезно с чисто эстетической точки зрения, но вряд ли полезно для успеха системы. Все эти проблемы описаны еще Ф. Бруксом в его «Мифическом человеко-месяце», где помимо описания этой проблемы даются и рекомендации по ее реше- нию. И хотя она вышла задолго до замечательной книги Энди Ханта и
Дэйва Томаса «Программист-прагматик» [34], Брукс дает тот же самый совет, что и постоянно звучит в книге прагматиков: не нужно крайно- стей, и больше прагматизма и самодисциплины.
Этот прагматизм проявится в разумном балансе причин изменения программного кода. Код лучше подвергать рефакторингу сразу, как только выявлена необходимость. Чем больше таких моментов проигно- рировано, тем сложнее получается ситуация для будущих рефакторин- гов. Появляются новые зависимости, и система становится более слож- ной. Каждое изменение в будущем придется более тщательно прове- рять. Не стоит забывать про баланс между желаниями команды разра- ботчиков и требованиями от заказчика по срокам.
Баланс заключается в том, чтобы разумно совмещать введение но- вых функций и рефакторинга. Во многих случаях это даже экономит время. Разработчику следует всегда отдавать себе отчет – тратить ли время на рефакторинг или же заняться новым функционалом. Заказчики легче расстаются с деньгами, чем с благодарностью за хорошо сделан- ную работу, тем более за качественный код, в котором они ничего не понимают. Иногда внутренняя красота кода нужна только честолюбию разработчика, и это может сыграть не в его пользу.
У. Апдайк, написавший главу в книге М. Фаулера [32], задается во- просом: почему разработчики не хотят применять рефакторинг к своим программам? Он считает, что если проект начинается с нуля (нет про- блемы совместимости с предыдущими версиями) и понятна задача, для


358 решения которой предназначена система, и тот, кто финансирует про- ект, готов поддерживать его, пока разработчик не будете удовлетворен результатами, то разработчику крупно повезло. Такой сценарий идеален для применения объектно-ориентированной технологии, но большинст- во может о нем только мечтать.
Значительно чаще предлагается расширить возможности уже имеющегося программного обеспечения. Если у разработчика далеко не полное понимание того, что он делает, или он поставлен в жесткие вре- менные рамки, что можно предпринять? Можно переписать программу заново. Применить свой опыт проектировщика, исправить все прежние грехи, работать творчески и с удовольствием. Но кто оплатит расходы?
И можно ли быть уверенным, что новая система сможет делать все то, что делала старая? Можно скопировать и модифицировать части суще- ствующей системы, чтобы расширить ее возможности. Это может пока- заться оправданным, и даже будет рассматриваться как демонстрация повторного использования кода. Не надо даже разбираться в том, что будет повторно использоваться.
Однако с течением времени происходит размножение ошибок, про- граммы разбухают, их архитектура разрушается, и нарастают дополни- тельные издержки на модификацию. Рефакторинг лежит посередине между этими двумя крайностями. Он дает возможность изменить суще- ствующее программное обеспечение, сделав понимание конструкции более явным, развить строение и извлечь повторно используемые ком- поненты, сделать яснее архитектуру программы, а также создать усло- вия, облегчающие ввод дополнительных функций. Рефакторинг может способствовать извлечению выгоды из сделанных ранее инвестиций, уменьшить дублирование и рационализировать программу.
Допустим, что разработчика привлекают эти преимущества. Он со- гласен с Ф. Бруксом в том, что модернизация представляет собой
«внутренне присущую сложность» разработки программного обеспече- ния. Он согласен с тем, что теоретически рефакторинг дает указанные преимущества. Почему же он все-таки не применяет рефакторинг к сво- им программам?
Возможны четыре причины:
1. Разработчик может не понимать, как выполнять рефакторинг.
2. Если выгоды ощутимы лишь в далекой перспективе, зачем тратить силы сейчас? В долгосрочной перспективе, когда придет время пожи- нать плоды, разработчик может оказаться вне проекта.
3. Рефакторинг кода является непроизводительной деятельностью, а разработчику платят за новые функции.
4. Рефакторинг может нарушить работу имеющейся программы.
Все это законные причины для беспокойства, отмечает У. Апдайк.
Ему не раз доводилось выслушивать их от персонала коммуникацион- ных и высокотехнологических компаний. Одни из них относятся к тех- нологиям, а другие – к администрированию. Все они должны быть рас- смотрены, прежде чем разработчики взвесят возможность рефакторинга


359 своего программного обеспечения. Разберемся с каждой из них по оче- реди.
Как и когда применять рефакторинг? Как можно научиться рефак- торингу? Каковы инструменты и технологии? В каких сочетаниях они могут принести какую-нибудь пользу? Когда следует их применять?
Ответы на эти вопросы, по мнению У. Апдайка, даны в книге М.
Фаулера [32]. В этой книге описано несколько десятков различных ре- факторингов, которыми автор пользуется в своей работе. Приведены примеры применения рефакторингов для внесения в программы важных изменений. С этим можно согласиться, тем не менее, в Интернете мож- но найти и отрицательные мнения об этой книге [2]. Однако, следует заметить, что негативное мнение связано с ожиданием рекомендаций архитектурного рефакторинга ПС, в то время как М. Фаулер в своей монографии говорит в основном о рефакторинге программного кода, причем чаще на уровне внутри класса и значительно реже на уровне классов. Рефакторинг архитектуры объектно-ориентированных ПС уде- лено внимания значительно меньше.
В работе [6] в довольно концентрированной форме даны ответы на основные вопросы, связанные с проведением рефакторинга ПС, а также ряд рекомендаций, как проводить рефакторинг. Обсуждаются следую- щие вопросы: цель рефакторинга, простые и сложные методы рефакто- ринга, когда начинать рефакторинг? Как начать рефакторинг? Как не поломать рабочий код? Когда рефакторинг не нужен? И в заключение дается каталог методов рефакторинга и примеры рефакторинга.
Перейдем к рассмотрению этих вопросов с позиций автора этой ра- боты.
Цель рефакторинга. Определите задачи, которые перед вами стоят, в большинстве случаев это упрощение ввода новых функций. Но, есть еще и другие цели. Профессиональные программисты, знакомые с пат- тернами проектирования [10], стараются привести структуру кода в по- рядок в соответствии с подходящими паттернами для улучшения под- держки, расширяемости и повторного использования кода.
Имеется также возможность использовать автоматизированные средства поиска кода подлежащего рефакторингу, это могут быть функ- ции с большим количеством аргументов или слишком длинные функ- ции. С помощью автоматических средств можно также выявлять струк- турное сходство различных методов. Такие функции являются кандида- тами на проведение рефакторинга.
Простые и сложные методы рефакторинга. Простые методы ре- факторинга – это базовые, фундаментальные методы, применяемые в рефакторинге. Например, это: выделение метода, перемещение поля класса, выделение класса, переименование метода, класса или поля и т.д. Простые методы рефакторинга – это фундамент, на основе которого строится любой вид рефакторинга. Нельзя начать рефакторинг, не зная фундаментальные методы. Сложные методы рефакторинга – это по сути комбинация простых методов, но решающих одну базовую задачу.
Примером сложного метода рефакторинга может быть разделение на-