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

Тип работы:
Контрольная
Предмет:
Программирование


Узнать стоимость

Детальная информация о работе

Выдержка из работы

Контрольная работа

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

Введение

рефакторинг экстремальный программирование

В рамках данной работы по курсу «Анализ и проектирование информационных систем» мы рассмотрим следующие вопросы — «Перепроектирование или рефакторинг кода» и «Управление исходным кодом. Коллективное владение кодом».

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

Объект исследования — анализ и проектирование информационных систем. Предметы исследования — рефакторинг и управление исходным кодом. Во введении содержатся ссылка на цель и задачи работы, объект и предмет исследования, а также кратко излагается назначение каждого из разделов. Работа содержит две смысловые части — реферат и развернутый ответ на исследуемый вопрос. Реферативная часть исследует теоретическое понятие «Перепроектирование или рефакторинг кода» на основе изученной литературы; развернутый ответ — предлагает авторский взгляд на вопрос «коллективного владения кодом».

Общие выводы и замечания содержатся в разделе «Заключение». Также приведен список использованных при написании работы источников.

1. Перепроектирование или рефакторинг кода

1.1 Понятие рефакторинга, причины рефакторинга

Рефакторинг (англ. refactoring) или реорганизация кода -- процесс изменения внутренней структуры программы, не затрагивающий её внешнего поведения и имеющий целью облегчить понимание её работы [1]. Мартин Фаулер в [4] приводит несколько иное определение рефакторинга: «Рефакторинг (англ. refactoring) -- изменение во внутренней структуре программного обеспечения, имеющее целью облегчить понимание его работы и упростить модификацию, не затрагивая наблюдаемого поведения». Как следует из приведенных определений, сферой применения рефакторинга является исходный код, основной целью -- упрощение будущих модификаций и облегчение понимания кода человеком.

Как правило, рефакторинг выполняется путем применения серии стандартизированных «микро-рефакторингов», каждый из которых является (обычно) незначительным изменением в исходном коде компьютерной программы, которое сохраняет поведение программного обеспечения, или, по крайней мере, не изменяет его соответствие функциональным требованиям [1].

Как уже отмечалось выше, основная цель рефакторинга заключается в облегчении понимания работы кода и упрощении будущих модификаций, но важно отметить, что рефакторинг — это не одно и то же, что и оптимизация производительности, и реинжиниринг. Оптимизация производительности -- модификация системы, имеющая целью улучшение её (системы) эффективности по некоторому набору критериев, и хотя оптимизация может и не изменять внутреннюю структуру программы, но часто усложняет и запутывает код, в отличие от рефакторинга [2]. Реинжиниринг программного обеспечения -- это процесс создания новой функциональности или устранения ошибок, путём революционного изменения, но используя уже имеющееся в эксплуатации программное обеспечение [3]. Таким образом, реинжиниринг может идти после рефакторинга, но сам по себе является переработкой кода с целью исправления ошибок и внедрения нового функционала, и не обязательно с целью упрощения понимания кода.

Отметим основные причины и предпосылки рефакторинга:

1. Рефакторинг улучшает композицию программного обеспечения.

По мере внесения в код изменений, связанных с реализацией краткосрочных целей или производимых без полного понимания организации кода, последний утрачивает свою структурированность. Регулярно проводимый рефакторинг помогает сохранять форму кода [4].

2. Рефакторинг облегчает понимание программного обеспечения.

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

3. Рефакторинг помогает находить ошибки.

При проведении рефакторинга кода разработчик должен глубоко вникать в него (как же иначе проводить рефакторинг?), пытаясь понять, что код делает, и достигнутое понимание, возвращая обратно в код, т. е. в уме «переписывая» оный на свой лад. После прояснения структуры программы некоторые сделанные программистом допущения становятся настолько ясными, что он не может не увидеть ошибки [4]. (Скорее здесь имеет место не рефакторинг в целом, а процесс размышления над кодом, просмотр оного [5], как один из элементов рефакторинга [прим. автора]).

4. Рефакторинг позволяет быстрее писать программы.

В конечном счете все перечисленное сводится к одному: рефакторинг способствует ускорению разработки кода. Общеизвестно, что хороший дизайн и архитектура важны для сохранения скорости разработки программного обеспечения. Иначе время программиста будет тратиться не на добавление новых функций, а на поиск и исправление ошибок. Благодаря рефакторингу программы разрабатываются быстрее, т. к. он удерживает композицию системы от распада. С его помощью можно даже улучшить дизайн [4].

5. Рефакторинг позволяет сохранять работоспособность и терпение.

«Я часто удивлялся тому, что побуждает меня реорганизовывать код. Конечно, я могу сказать, что реорганизую код, чтобы убрать из него повторы, упростить и сделать понятным. Но что на самом деле побуждает меня к реорганизации? Эмоции. Часто я занимаюсь рефакторингом только для того, чтобы работа с кодом была менее раздражающей.» [6]

1.2 Принципы и методы рефакторинга

Кратко опишем основополагающие принципы рефакторинга, (они же три правила в [4]):

1. Применяйте рефакторинг при добавлении новой функции.

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

2. Применяйте рефакторинг, если требуется исправить ошибку.

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

3. Применяйте рефакторинг при разборе кода.

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

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

В рамках объема данной работы не представляется возможным охарактеризовать все возможные методы рефакторинга, поэтому кратко остановимся на наиболее используемых, приводимых в [6, 1]:

1. Изменение сигнатуры метода (Change Method Signature)

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

2. Инкапсуляция поля (Encapsulate field)

В случае, если у класса имеется открытое поле, необходимо сделать его закрытым и обеспечить методы доступа. После «Инкапсуляции поля» часто применяется «Перемещение метода».

3. Выделение метода (Extract Method)

Выделение метода заключается в выделении из длинного и/или требующего комментариев кода отдельных фрагментов и преобразовании их в отдельные методы, с подстановкой подходящих вызовов в местах использования. В этом случае действует правило: если фрагмент кода требует комментария о том, что он делает, то он должен быть выделен в отдельный метод. Также правило: один метод не должен занимать более чем один экран (25−50 строк, в зависимости от условий редактирования), в противном случае некоторые его фрагменты имеют самостоятельную ценность и подлежат выделению. Из анализа связей выделяемого фрагмента с окружающим контекстом делается вывод о перечне параметров нового метода и его локальных переменных.

4. Перемещение метода (Move Method)

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

5. Замена условного оператора полиморфизмом (Replace Conditional with Polymorphism)

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

Многие из методов рефакторинга автоматизированы и соотвествующий функционал встроен в средства разработки или расширения к ним, например [8, 9]. Это позволяет упростить процесс изменения кода и повысить его качество за счет снижения необходимости ручной правки оного. В то же время многие среды разработки поддерживают и средства тестирования, что позволяет сразу же применять тесты к изменяемому коду, проверяя соблюдение принципа постоянства поведения кода как основы понятия «рефакторинг», например [10, 11]. Подробнее об этом читайте в главе «Тестирование при рефакторинге».

В данной главе дается лишь краткое описание основных методов рефакторинга, за подробной информацией об оных советуем вам обратится к источникам [4, 6, 7].

1.3 Признаки «плохого» кода

Код с запашком (код с душком, дурно пахнущий код англ. code smell) -- термин, обозначающий код с признаками (запахами) проблем в системе, был введен Кентом Беком и использован Мартином Фаулером в его книге «Рефакторинг. Улучшение существующего кода» [4], с тех пор активно используется разработчиками ПО [1].

Таким образом, такой код может являться первым признаком необходимости проведения рефакторинга, индикатором общего состояния кода. Но, следует отметить, что код, содержащий возможные признаки проблем, не всегда является таковым, и в каждом конкретном случае необходим анализ как самого кода, так и его контекста для вынесения окончательного решения о необходимости рефакторинга [4, 6]. Примерами такого «ложно-проблемного» кода может являться высокооптимизированный код, когда в приоритете над читаемостью, расширяемостью и простотой находится быстродействие и потребление ресурсов разрабатываемым ПО, а также некоторые вырожденные случаи (поразмышляйте над потенциальными местами оного [прим. автора]).

Не существует четких правил поиска «ложно-проблемного» кода, т.к., как уже отмечалось выше, само это понятие применимо в зависимости от контекста. В работе [4] выделен общий список мест, на которые разработчик должен обращать внимание, и которые могу сигнализировать о проблемах кода. Приведем некоторые:

Дублирование кода -- одинаковые структуры кода в нескольких местах. Объединение этих структур позволит улучшить программный код.

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

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

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

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

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

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

Временное поле -- это поля, которые нужны объекту только при определенных обстоятельствах. Такое положение вещей трудно для понимания, так как ожидается, что объекту нужны все его поля [12].

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

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

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

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

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

Выше изложены лишь основные характерные черты «проблемного» кода, за детальной информацией как по «плохому» коду, так и по методам рефакторинга, применяемым для его устранения, обращайтесь к [4, 6, 7].

1.4 Тестирование при рефакторинге

Прежде чем рассмотреть тестирование при рефакторинге, напомним еще раз первоначальное определение: «Рефакторинг (англ. refactoring) -- изменение во внутренней структуре программного обеспечения, имеющее целью облегчить понимание его работы и упростить модификацию, не затрагивая наблюдаемого поведения» [4].

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

При зрелом размышлении одним из простейших методов проверки инвариантности поведения представляется тестирование. Как пишет М. Фаулер в [4]: «При проведении рефакторинга важным предварительным условием является наличие надежных тестов». Из этого следует, что еще до процесса рефакторинга код уже должен содержать тесты, т.к. именно они являются индикатором корректности вносимых изменений.

В принципе, процесс разработки в таком случае предельно ясен — изначально составляется комплект тестов, на его основе пишется работающий код (новый функционал). «Когда вы пишете тест, то спрашиваете себя, что нужно сделать для добавления этой функции. При написании теста вы также сосредоточиваетесь на интерфейсе, а не на реализации, что всегда полезно. Это также значит, что появляется четкий критерий завершения кодирования — в итоге тест должен работать» [4]. Предыдущий тезис во многом созвучен идее частого тестирования, как важной части экстремального программирования [13].

При рефакторинге тесты позволяют верифицировать изменения и, соответственно легко находить ошибки. Таким образом, развивая данную мысль, мы естественным образом получаем такую методологию разработки, как Test Driven Development [15].

Конечно, нельзя доказать отсутствие в программе ошибок путем тестирования. Это верное замечание, но оно не умаляет способности тестирования повысить скорость программирования. В [4] дается соответствующий совет по методике создания тестов: «Необходимо сконцентрироваться на подозрительных местах. Изучите код и определите, где он становится сложным. Изучите функцию и установите области, где могут возникнуть ошибки. Тесты не выявят все ошибки, но во время рефакторинга можно лучше понять программу и благодаря этому найти больше ошибок».

2. Кодирование, рефакторинг и управление исходным кодом. Коллективное владение кодом. Развернутый ответ

Коллективное владение кодом — это один из приемов «Экстремального программирования» (XP), гибкой методологии разработки ПО [13]. Суть его заключается в том, чтобы стимулировать разработчиков делиться идеями для всех частей проекта, а не только для модулей в своей зоне ответственности. А также любой разработчик может изменять любой код для расширения функциональности, исправления ошибок или рефакторинга.

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

Для того, чтобы глубже понять смысл понятия «Коллективное владение кодом», нужно рассмотреть его в контексте самого XP. Работа c кодом начинается с создания тестов модуля, т. е. тесты должны предшествовать программированию модуля. Тесты помещаются в библиотеку кодов вместе с кодом, который они тестируют. Тесты делают возможным коллективное создание кода и в некоторой мере защищают код от неожиданных изменений. В случае обнаружения ошибки также создается соответствующий тест, чтобы предотвратить ее повторное появление. Все это общеизвестные истины [14].

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

Основываясь на результатах тестирования разработчики включают в очередную итерацию работу над ошибками. Из вышеперечисленного следует вывод о том, что тестирование является одним из краеугольных камней ХР [14].

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

Во время очередной итерации всех сотрудников перемещают на новые участки работы. Такие перемещения помогают устранить изоляцию знаний и «узкие места». Особенно полезна смена одного из разработчиков при парном программировании.

Вообще, многие программисты -- консерваторы. Они продолжают использовать код, который трудно сопровождать, только потому, что он все еще работает («Работает, не трожь!» [прим. автора]). Со временем это приводит к понижению общей эффективности систем. В ХР же считается, что код нужно постоянно обновлять -- удалять лишние части, убирать ненужную функциональность. Этот процесс называют реорганизацией кода (refactoring, cм. главу «Перепроектирование или рефакторинг кода»). Реорганизация поддерживает прозрачность и целостность кода, обеспечивает его легкое понимание, исправление и расширение. На реорганизацию уходит значительно меньше времени, чем на сопровождение устаревшего кода.

И еще одна составляющая коллективного владения кодом -- непрерывная интеграция. Без последовательной и частой интеграции результатов в систему разработчики не могут быть уверены в правильности своих действий. Кроме того, трудно вовремя оценить качество выполненных фрагментов проекта и внести необходимые коррективы. Интеграция позволяет объединить усилия отдельных пар и стимулирует повторное использование кода [14].

Заключение

«Рефакторинг (англ. refactoring) -- изменение во внутренней структуре программного обеспечения, имеющее целью облегчить понимание его работы и упростить модификацию, не затрагивая наблюдаемого поведения» [4]. Как следует из приведенных определений, сферой применения рефакторинга является исходный код, основной целью -- упрощение будущих модификаций и облегчение понимания кода человеком.

Индикатором необходимости в рефакторинге является «проблемный код», но, следует отметить, что код, содержащий возможные признаки проблем, не всегда является таковым, и в каждом конкретном случае необходим анализ как самого кода, так и его контекста для вынесения окончательного решения о необходимости рефакторинга [4, 6].

Рефакторинг советуется применять тогда, когда добавляется новая функция, исправляется ошибка или проводится разбор кода. Существует набор методов рефакторинга, знание которых необходимо для корректного его применения [4, 6]. Многие среды разработки содержат средства автоматизации рефакторинга.

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

Достоинства проведения рефакторинга: улучшение композиции программного

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

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

Коллективное владение кодом заключается в том, чтобы стимулировать разработчиков делиться идеями для всех частей проекта, а не только для модулей в своей зоне ответственности. Коллективное владение кодом как часть XP [13] позволяет ускорить процесс как разработки, так и исправления ошибок засчет глубокого знания системы каждым программистом и позволяет выполнять задачи более качественно за счет участия множества разработчиков в проверке и модификации кода. С другой стороны, коллективное владение привносит риски нарушения существующего функционала, т.к. Unit-тесты, создаваемые для проверки изменений, не могут дать 100% гарантии корректности кода в общем случае, и порождает проблемы затрат времени на принятие единых решений в средах с большим количеством разработчиков.

Применение методологий XP, TDD, техники рефакторинга в проектах разработки программного обеспечения необходимо обуславливать критериями и факторами, которые зависят от области применения и проекта. Бездумное подражание «современным стилям» и «новейшим методологиям» является одним из факторов, сдерживающих качественное развитие знаний и навыков сообщества разработчиков, но постепенное и вдумчивое изучение и применение данных стилей и методологий полезно для профессионального совершенствования разработчика.

Список использованных источников

1. Википедия / Рефакторинг кода [Электронный ресурс]. — Режим доступа: https: //en. wikipedia. org/wiki/Code_refactoring. — Дата доступа: 25. 11. 2013.

2. Википедия / Оптимизация [Электронный ресурс]. — Режим доступа: https: //ru. wikipedia. org/wiki/Оптимизация_кода. — Дата доступа: 25. 11. 2013.

3. Википедия / Реинжиниринг программного обеспечения [Электронный ресурс]. — Режим доступа: https: //ru. wikipedia. org/wiki/Реинжиниринг программного обеспечения. — Дата доступа: 25. 11. 2013.

4. Фаулер М., Бек К., Брант Д., Робертс Д., Апдайк У. Рефакторинг: улучшение существующего кода / Refactoring: Improving the Design of Existing Code (2000). -- Спб: Символ-Плюс, 2009. -- 432 с. -- 3000 экз.

5. Википедия / Просмотр кода [Электронный ресурс]. — Режим доступа: https: //ru. wikipedia. org/wiki/Просмотр_кода. — Дата доступа: 25. 11. 2013.

6. Джошуа Кериевски Рефакторинг с использованием шаблонов / Refactoring to Patterns. -- Вильямс, 2008. -- 400 с.

7. Скотт В. Эмблер, Прамодкумар Дж. Садаладж Рефакторинг баз данных: эволюционное проектирование / Refactoring Databases: Evolutionary Database Design (Addison-Wesley Signature Series). -- М.: «Вильямс», 2007. -- 368 с.

8. Microsoft Developer Networks / Рефакторинг (С#) [Электронный ресурс]. — Режим доступа: http: //msdn. microsoft. com/ru-ru/library/719exd8s. aspx. — Дата доступа: 25. 11. 2013.

9. IBM / Рефакторинг для всех [Электронный ресурс]. — Режим доступа: http: //www. ibm. com/developerworks/ru/library/os-ecref/

10. Microsoft Developer Networks / Проверка кода при помощи модульных тестов [Электронный ресурс]. — Режим доступа: http: //msdn. microsoft. com/ru-ru/library/dd264975. aspx. — Дата доступа: 25. 11. 2013.

11. IBM / Модульное тестирование в Eclipse [Электронный ресурс]. — Режим доступа: http: //www. ibm. com/developerworks/ru/library/os-eclipse-rmock/. — Дата доступа: 25. 11. 2013.

12. Codingcraft / Временное поле (Temporary Field) [Электронный ресурс]. — Режим доступа: http: //codingcraft. ru/resources/REFACTORING/TemporaryField. htm. — Дата доступа: 25. 11. 2013.

13. Кент Бек: Экстремальное программирование / Extreme Programming Explained. -- Изд.: «Питер», 2002. -- 224 с.

14. 2programmer / Процесс разработки — Коллективное владение кодом [Электронный ресурс]. — Режим доступа: http: //2programmer. ru/tehnolog6?start=16. — Дата доступа: 25. 11. 2013.

15. Википедия / Разработка через тестирование [Электронный ресурс]. — Режим доступа: https: //ru. wikipedia. org/wiki/Разработка через тестирование. — Дата доступа: 25. 11. 2013.

ПоказатьСвернуть
Заполнить форму текущей работой