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

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

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

Добавлен: 06.11.2023

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

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

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

376 ются ненужными. Если для объяснения действий блока требуется ком- ментарий, он рекомендует применить выделение метода. Если метод уже выделен, но по-прежнему нужен комментарий для объяснения его действия, воспользуйтесь переименованием метода. А если требуется изложить некоторые правила, касающиеся необходимого состояния системы, примените Введение утверждения (Introduce Assertion).
Почувствовав потребность написать комментарий, попробуйте сна- чала изменить структуру кода так, чтобы любые комментарии стали излишними. Комментарии полезны, когда программист не знает, как поступить. Помимо описания происходящего, комментарии могут отме- чать те места, в которых программист не уверен. Правильным будет поместить в комментарии обоснование своих действий. Это пригодится тем, кто будет модифицировать код в будущем.
8.4. Уровни рефакторинга
По определению, рефакторинг – процесс изменения внутренней структуры программы, не затрагивающий её внешнего поведения и имеющий целью облегчить понимание её работы. В основе рефакторин- га лежит последовательность небольших эквивалентных (то есть сохра- няющих поведение) преобразований. Иногда понятия рефакторинга за- ключается в определении регламентированного способа реструктуриза- ции кода с применением небольших итерационных шагов. Прежде все- го, рефакторинг обеспечивает постепенное развитие кода во времени, в результате чего реализуется эволюционный подход к программирова- нию. Особенностью рефакторинга является то, что он сохраняет функ- циональную семантику базового кода. Многие разработчики, органи- зующие свою работу на принципах адаптивного программирования, и, в частности, как отмечено выше, специалисты по экстремальному про- граммированию полагают, что рефакторинг является ведущим подхо- дом к разработке. На практике можно столь же часто встретить приме- ры применения рефакторинга небольших фрагментов кода, как и введе- ния операторов if или циклов [35].
Первым уровнем рефакторинга можно считать такое изменение ко- да, которое не затрагивает структуру классов (количество и взаимосвя- зи, интерфейсы) объектно-ориентированной программной системы, т.е. рефакторингу подвергается программный код внутри классов. Это мо- гут быть методы и алгоритмы, реализуемые методами, поля и т.п. Мно- гие разработчики программного обеспечения считают, что при рефакто- ринге лучше полагаться на интуицию, основанную на опыте, но можно выделить наиболее очевидные причины, когда код нужно подвергнуть процессу рефакторинга. Сюда относится дублирование фрагментов кода, длинный метод реализации, использование достаточно длинного списка параметров, применение и использование избыточных времен- ных переменных, изменение сигнатуры метода, инкапсуляция поля, за- мена условного оператора полиморфизмом и т.п.


377
Второй уровень рефакторинга относится к изменению структуры классов программной системы, добавлению новых классов, выделению и разбиению больших классов, переносу или добавлению новых мето- дов, выделению интерфейсов и др. Основными стимулами его проведе- ния являются следующие задачи: необходимо добавить новую функцию, которая не достаточно укла- дывается в принятое архитектурное решение программного модуля; необходимо исправить ошибку, причины возникновения которой не выделены четко структурированной базовой внешней формой; проблематика в командной разработке, которая обусловлена слож- ностью логики программного продукта.
Современные приложения имеют сложную и многослойную архи- тектуру. Рефакторинг архитектуры программных систем является третьим уровнем рефакторинга. Трудно представить сегодня серьезное коммерческое приложение, не взаимодействующее с базой данных (БД), что подразумевает уровень архитектуры, обеспечивающий взаимодей- ствие с БД. Рефакторинг этого уровня – довольно трудоемкий процесс.
Операции рефакторинга БД концептуально являются более сложны- ми, чем операции рефакторинга кода, поскольку при проведении опера- ций рефакторинга кода необходимо заботиться лишь о сохранении функциональной семантики структуры фрагментации, а при осуществ- лении операций рефакторинга БД возникает процесс необходимости и сохранение информационной семантики. Достаточно важным фактором является то, что усложнение операций рефакторинга БД может быть обусловлено наличием большого количества связей, поддерживаемых архитектурой БД . Рефакторинг кода может быть связан не только с из- начальной архитектурной проблематикой. Процесс необходимо осуще- ствлять после комплексного анализа структуры и выявления «фрагмен- тарных участков, необходимых для оптимизации со стороны команды разработчиков». В этом случае проводится рефакторинг «фрагментар- ного» участка программы. Профилировка поможет его определить.
При осуществлении процесса рефакторинга необходимо четко пред- ставлять, какой из методов более лучший и оптимальный для данной структуры, ведь нелогичное использование методов приведет к трудно- стям работы программы. Опытные разработчики четко представляют и делят процесс оптимизации кода и процесс рефакто- ринга. При процессе рефакторинга разработчик старается сделать так, чтобы код стал удобнее для понимания и его поддержки, а при оптими- зации кода приходится делать процедуры, которые приводят к обратно- му эффекту, что приводит к проблемам читаемости кода, но за счет это- го возрастает скорость его выполнения.
1   ...   29   30   31   32   33   34   35   36   37

8.5. Методы рефакторинга
8.5.1. Основные методы
К наиболее часто употребляемым методам рефакторинга можно от- нести [17]:

378 изменение сигнатуры метода (Change Method Signature); инкапсуляция поля (Encapsulate Field); выделение класса (Extract Class); выделение интерфейса (Extract Interface); выделение локальной переменной (Extract Local Variable); выделение метода (Extract Method); генерализация типа (Generalize Type); встраивание (Inline); введение фабрики (Introduce Factory); введение параметра (Introduce Parameter); подъём поля/метода (Pull Up); спуск поля/метода (Push Down); замена условного оператора полиморфизмом (Replace Conditional with Polymorphism).
Пожалуй, наилучшие рекомендации и практические советы по опи- санию и иллюстрации использования методов рефакторинга программ- ного кода в объектно-ориентированных программах можно найти в мо- нографии М. Фаулера [33].
Рефакторинг кода должен осуществляться до полного исчерпания его возможностей, поскольку наибольшая производительность может быть достигнута только в условиях работы с исходным кодом макси- мально высокого качества. При возникновении необходимости добавить к коду новые возможности следует оценить качество реплицируемого кода в аспектах данного проекта, что позволит успешно реализовать требуемые средства. Если ответ на этот вопрос является положитель- ным, то можно приступать к добавлению новых функциональных средств. При отрицательном решении данного аспектного внедрения код вначале должен быть подвергнут рефакторингу, для того чтобы он имел оптимальный вариант, и только после этого возможно осуществ- ление процесса добавления новых функций. Применение указанного подхода приводит к значительному увеличению объема работы, но практика показывает, что если доработка начинается с высококачест- венного исходного кода, после чего постоянно осуществляется рефак- торинг этого кода для поддержки его в том же состоянии, то все новые замыслы реализуются чрезвычайно показательно и эффективно.
Рассмотрим основные методы рефакторинга, приведенные в моно- графии М. Фаулера и ряде статей, опубликованных в Интернете [17, 21
– 27, 29]. При рассмотрении этих методов не будем останавливаться на аргументации необходимости их применения и технике исполнения.
Это потребовало бы много времени и места в данной книге, и не являет- ся ее задачей. Желающих получить более подробную информацию и рассмотреть конкретные примеры рекомендуется обратиться к литера- туре, перечень которой дан в конце главы.

379 8.5.2. Рефакторинг кода первого уровня
Как отмечает М. Фаулер, значительная часть его рефакторингов за- ключается в составлении методов, оформляющих код правильным обра- зом. Почти всегда проблемы возникают из-за слишком длинных мето- дов, часто содержащих массу информации, погребенной под сложной логикой. Основным типом рефакторинга (всегда самого популярного) в этих случаях служит Выделение метода (Extract Method), в результате которого фрагмент кода превращается в отдельный метод. Встраивание
метода (Inline Method) , по существу, является противоположной про- цедурой: вызов метода заменяется при этом кодом, содержащимся в теле. Встраивание метода требуется после проведения нескольких вы- делений, когда видно, что какие-то из полученных методов больше не выполняют свою долю работы или требуется реорганизовать способ разделения кода на методы.
Самая большая проблема Выделения метода связана с обработкой локальных переменных и, прежде всего, с наличием временных пере- менных. Работая над методом, с помощью Замены временной перемен-
ной вызовом метода (Replace Temp with Query) нужно избавиться от возможно большего количества временных переменных. Если времен- ная переменная используется для разных целей, Фаулер рекомендует сначала применить Расщепление временной переменной (Split Temporary
Variable), чтобы облегчить последующую ее замену.
Однако иногда временные переменные оказываются слишком слож- ными для замены. Тогда требуется Замена метода объектом метода
(Replace Method with Method Object). Это позволяет разложить даже самый запутанный метод, но ценой введения нового класса для выпол- нения задачи. С параметрами меньше проблем, чем с временными пере- менными, при условии, что им не присваиваются значения. В против- ном случае требуется выполнить Удаление присваиваний параметрам
(Remove Assignments to Parameters).
Когда метод разложен, значительно легче понять, как он работает.
Иногда обнаруживается возможность улучшения алгоритма, позволяю- щая сделать его более понятным. Тогда нужно применить Замещение
алгоритма (Substitute Algorithm).
Выделение метода заключается в выделении из длинного и/или тре- бующего комментариев кода отдельных фрагментов и в преобразовании их в отдельные методы, с подстановкой подходящих вызовов в местах использования. В этом случае действует правило: если фрагмент кода требует комментария о том, что он делает, то он должен быть выделен в отдельный метод. Также правило: один метод не должен занимать более чем один экран (25-50 строк, в зависимости от условий редактирова- ния), в противном случае некоторые его фрагменты имеют самостоя- тельную ценность и подлежат выделению. Из анализа связей выделяе- мого фрагмента с окружающим контекстом делается вывод о перечне параметров нового метода и его локальных переменных.


380
В простейшем случае выделение метода выполняется тривиально. В чем может быть проблема? В локальных переменных – параметрах, пе- редаваемых в исходный метод, и временных переменных, объявленных в исходном методе. Локальные переменные действуют только в исход- ном методе, поэтому при выделении метода с ними связана дополни- тельная работа. В некоторых случаях они даже вообще не позволят вы- полнить рефакторинг. Проще всего, когда локальные переменные чита- ются, но не изменяются. В этом случае можно просто передавать их в качестве параметров. То же применимо, если локальная переменная яв- ляется объектом и вызывается метод, модифицирующий переменную. В этом случае также можно передать объект в качестве параметра. Другие меры потребуются, только если действительно выполняется присваива- ние локальной переменной.
Сложности возникают, когда локальным переменным присваивают- ся новые значения. В данном случае имеются в виду только временные переменные. Увидев в коде присваивание параметру, нужно применить
Удаление присваиваний параметрам (Remove Assignments to Parametrs).
Есть два случая присваивания временным переменным. В простейшем случае временная переменная используется лишь внутри выделенного кода. Если это так, временную переменную можно переместить в выде- ленный код. Другой случай – использование переменной вне этого кода.
В этом случае необходимо обеспечить возврат выделенным кодом мо- дифицированного значения переменной.
Может возникнуть вопрос, что произойдет, если надо возвратить не одну, а несколько переменных. Здесь есть ряд вариантов. Обычно лучше всего выбрать для выделения другой код. Многие, в том числе М. Фау- лер, предпочитают, чтобы метод возвращал одно значение, поэтому нужно попытался организовать возврат разных значений разными мето- дами. (Если язык программирования допускает выходные параметры, можно этим воспользоваться и по возможности стараться работать с одиночными возвращаемыми значениями.)
Часто временных переменных так много, что выделять методы ста- новится очень трудно. В таких случаях нужно пытаться сократить число временных переменных с помощью Замены временной переменной вы-
зовом метода (Replace Temp with Query). Если и это не помогает, реко- мендуется прибегнуть к замене метода объектом методов. Для послед- него рефакторинга безразлично, сколько имеется временных перемен- ных и какие действия с ними выполняются.
Встраивание метода (Inline Method). Если тело метода столь же по- нятно, как и его название, имеет смысл поместить тело метода в код, который его вызывает, и удалить метод. Правильно использовать корот- кие методы с названиями, отражающими их назначение, это что приво- дит к получению более понятного и легкого для чтения кода. Но иногда встречаются методы, тела которых столь же прозрачны, как названия, либо изначально, либо становятся таковыми в результате рефакторинга.
В этом случае необходимо избавиться от метода. М. Фаулер рекоменду- ет обращаться к встраиванию метода, когда в коде слишком много кос-


381 венности и оказывается, что каждый метод просто выполняет делегиро- вание другому методу, и во всем этом делегировании можно просто за- блудиться.
Встраивание временной переменной (Inline Temp). Если имеется временная переменная, которой один раз присваивается простое выра- жение, и эта переменная мешает проведению других рефакторингов, можно заменить этим выражением все ссылки на данную переменную.
Чаще всего встраивание переменной производится в ходе Замены вре-
менной переменной вызовом метода (Replace Temp with Query), поэто- му подлинную мотивировку следует искать там. Встраивание времен- ной переменной выполняется самостоятельно только тогда, когда обна- руживается временная переменная, которой присваивается значение, возвращаемое вызовом метода. Часто эта переменная безвредна, и мож- но оставить ее в покое. Но если она мешает другим рефакторингам, на- пример, выделению метода, ее надо встроить.
Простыми случаями данного рефакторинга являются такие, в кото- рых присваивание временным переменным осуществляется однократно, и те, в которых выражение, участвующее в присваивании, свободно от побочных эффектов. Остальные ситуации сложнее, но разрешимы. Об- легчить положение могут предварительное Расщепление временной пе-
ременной (Split Temporary Variable) и Разделение запроса и модифика-
тора (Separate Query from Modifier). Если временная переменная слу- жит для накопления результата (например, при суммировании в цикле), соответствующая логика должна быть воспроизведена в методе запроса.
Введение поясняющей переменной (Introduce Explaining Variable).
Пусть имеется сложное выражение, трудное для чтения. В таких ситуа- циях полезно с помощью временных переменных превратить выраже- ние в нечто, лучше поддающееся управлению. Особую ценность введе- ние поясняющей переменной имеет в условной логике, когда удобно для каждого пункта условия объяснить, что он означает, с помощью временной переменной с хорошо подобранным именем. Другим приме- ром служит длинный алгоритм, в котором каждый шаг можно раскрыть с помощью временной переменной.
Расщепление временной переменной (Split Temporary Variable). Вре- менные переменные создаются с различными целями. Иногда эти цели естественным образом приводят к тому, что временной переменной не- сколько раз присваивается значение. Переменные управления циклом изменяются при каждом проходе цикла (например, i в for (int i=0; i<10; i++)). Накопительные временные переменные аккумулируют некоторое значение, получаемое при выполнении метода. Если имеется временная переменная, которой неоднократно присваивается значение, но это не переменная цикла и не временная переменная для накопления результа- та, то следует создать для каждого присваивания отдельную временную переменную.
Удаление присваиваний параметрам (Remove Assignments to Parame- ters). Код выполняет присваивание параметру. Под этим подразумевает- ся, что если в качестве значения параметра передается объект с именем