Главная страница

Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по


Скачать 5.88 Mb.
НазваниеРуководство по стилю программирования и конструированию по
АнкорСовершенный код
Дата31.03.2023
Размер5.88 Mb.
Формат файлаpdf
Имя файлаСовершенный код. Мастер-класс. Стив Макконнелл.pdf
ТипРуководство
#1028502
страница71 из 106
1   ...   67   68   69   70   71   72   73   74   ...   106
ГЛАВА 24 Рефакторинг
563
вильная абстракция взаимодействия Класса A с Классом B. Если за вызов C дол#
жен отвечать B, внесите нужные изменения.
Удаление посредника Если Класс A вызывает Класс B, а Класс B вызывает Класс
C, подумайте, не лучше ли вызывать C непосредственно из A. Целесообразность делегирования полномочий Классу B зависит от того, улучшит ли это целостность его интерфейса или ухудшит.
Замена наследования на делегирование Если из одного класса нужно исполь#
зовать другой класс, но вы хотите получить больший контроль над интерфейсом второго класса, сделайте суперкласс полем бывшего подкласса и создайте для доступа к нему набор открытых методов, формирующих связную абстракцию.
Замена делегирования на наследование Если класс предоставляет доступ ко всем открытым методам класса#делегата (класса#члена), выполните наследование от класса#делегата, а не просто используйте его.
Создание внешнего метода Если в класс нужно включить дополнительный ме#
тод, но изменять класс нельзя, вы можете создать нужный метод в клиентском классе.
Создание класса'расширения Если в класс нужно включить несколько допол#
нительных методов, но изменять класс нельзя, вы можете создать новый класс,
объединяющий функциональность неизменяемого класса с дополнительной функ#
циональностью. Для этого вы можете или выполнить наследование от исходного класса и добавить новые методы в подклассы, или заключить класс в оболочку,
предоставив доступ к нужным методам.
Инкапсуляция открытой переменной'члена Если данные#члены открыты,
сделайте их закрытыми и реализуйте доступ к ним при помощи методов.
Удаление методов установки значений неизменяемых полей Если поле предполагается устанавливать во время создания объекта и не изменять впослед#
ствии, инициализируйте поле в конструкторе объекта и не создавайте вводящий в заблуждение метод
Set().
Сокрытие методов, которые не следует вызывать извне класса Если без метода интерфейс класса будет более согласованным, скройте метод.
Инкапсуляция неиспользуемых методов Если обычно вы используете толь#
ко часть интерфейса класса, создайте новый интерфейс, предоставляющий до#
ступ только к необходимым методам. Убедитесь в том, что новый интерфейс фор#
мирует согласованную абстракцию.
Объединение суперкласса и подкласса, имеющих очень похожую реали'
зацию Если степень специализации подкласса невысока, объедините его с су#
перклассом.
Рефакторинг на уровне системы
Создание эталонного источника данных, которые вы не можете конт'
ролировать Иногда какие#то данные трудно согласованно использовать из дру#
гих объектов, которым нужны эти данные. В качестве примера можно привести данные элемента управления с GUI#интерфейсом. В этом случае вы можете создать

564
ЧАСТЬ V Усовершенствование кода класс, воспроизводящий данные элемента управления, и рассматривать этот класс как эталонный источник данных и для элемента управления, и для другого кода.
Изменение однонаправленной связи между классами на двунаправленную
Если два класса должны использовать возможности друг друга, но только один класс знает о другом классе, измените классы так, чтобы они оба знали друг о друге.
Изменение двунаправленной связи между классами на однонаправленную
Если два класса известны друг другу, но на самом деле только один класс должен знать о другом, измените характер связи между классами.
Предоставление фабричного метода вместо простого конструктора Ис#
пользуйте фабричный метод, если вам нужно создавать объекты на основе кода типа или если вы хотите работать с объектами#ссылками, а не объектами#значениями.
Замена кодов ошибок на исключения или наоборот Убедитесь, что вы ис#
пользуете стандартный подход к обработке ошибок, основанный на той или иной стратегии.
Контрольный список: виды рефакторинга
Рефакторинг на уровне данных
 Замена магического числа на именованную константу.
 Присвоение переменной более ясного или информативного имени.
 Встраивание выражения в код.
 Замена выражения на вызов метода.
 Введение промежуточной переменной.
 Преобразование многоцелевой переменной в несколько одноцелевых пере- менных.
 Использование локальной переменной вместо параметра.
 Преобразование элементарного типа данных в класс.
 Преобразование набора кодов в класс или перечисление.
 Преобразование набора кодов в класс, имеющий производные классы.
 Преобразование массива в класс.
 Инкапсуляция набора.
 Замена традиционной записи на класс данных.
Рефакторинг на уровне отдельных операторов
 Декомпозиция логического выражения.
 Вынесение сложного логического выражения в грамотно названную булеву функцию.
 Консолидация фрагментов, повторяющихся в разных частях условного опе- ратора.
 Использование оператора break / return вместо управляющей переменной цикла.
 Возврат из метода сразу после получения ответа вместо установки возвра- щаемого значения внутри вложенных операторов if-then-else.
 Замена условных операторов (особенно многочисленных блоков case) на вызов полиморфного метода.
 Создание и использование «пустых» объектов вместо проверки того, равно ли значение null.
http://cc2e.com/2450

ГЛАВА 24 Рефакторинг
565
Рефакторинг на уровне отдельных методов
 Извлечение метода из другого метода.
 Встраивание кода метода.
 Преобразование объемного метода в класс.
 Замена сложного алгоритма на простой.
 Добавление параметра.
 Удаление параметра.
 Отделение операций запроса данных от операций изменения данных.
 Объединение похожих методов при помощи их параметризации.
 Разделение метода, поведение которого зависит от полученных параметров.
 Передача в метод целого объекта вместо отдельных полей.
 Передача в метод отдельных полей вместо целого объекта.
 Инкапсуляция нисходящего приведения типов.
Рефакторинг реализации классов
 Замена объектов-значений на объекты-ссылки.
 Замена объектов-ссылок на объекты-значения.
 Замена виртуальных методов на инициализацию данных.
 Изменение положения методов-членов или данных-членов в иерархии на- следования.
 Перемещение специализированного кода в подкласс.
 Объединение похожего кода и его перемещение в суперкласс.
Рефакторинг интерфейсов классов
 Перемещение метода в другой класс.
 Разделение одного класса на несколько.
 Удаление класса.
 Сокрытие делегата.
 Удаление посредника.
 Замена наследования на делегирование.
 Замена делегирования на наследование.
 Создание внешнего метода.
 Создание класса-расширения.
 Инкапсуляция открытой переменной-члена.
 Удаление методов установки значений неизменяемых полей.
 Сокрытие методов, которые не следует вызывать извне класса.
 Инкапсуляция неиспользуемых методов.
 Объединение суперкласса и подкласса, имеющих очень похожую реализацию.
Рефакторинг на уровне системы
 Создание эталонного источника данных, которые вы не можете контроли- ровать.
 Изменение однонаправленной связи между классами на двунаправленную.
 Изменение двунаправленной связи между классами на однонаправленную.
 Предоставление фабричного метода вместо простого конструктора.
 Замена кодов ошибок на исключения или наоборот.

566
ЧАСТЬ V Усовершенствование кода
24.4. Безопасный рефакторинг
Рефакторинг — эффективный способ повышения качества кода. Но, как и все эффективные инструменты, при невер#
ном использовании он может причинить вред. Несколько простых советов помогут предотвратить неверное приме#
нение рефакторинга.
Сохраняйте первоначальный код Перед началом рефак#
торинга убедитесь, что вы сможете вернуться к коду, с ко#
торого начинаете. Сохраните код в системе управления вер#
сиями или скопируйте корректные файлы в резервный ка#
талог.
Стремитесь ограничить объем отдельных видов рефакторинга Некото#
рые виды рефакторинга масштабнее других, к тому же не всегда можно точно ска#
зать, что именно составляет «один вид рефакторинга». Чтобы четко представлять все следствия вносимых изменений, не раздувайте виды рефакторинга. Примеры соблюдения этого принципа см. в книге «Refactoring» (Fowler, 1999).
Выполняйте отдельные виды рефакторинга по одному за раз Некоторые виды рефакторинга сложнее других. За исключением самых простых случаев выполняйте все виды рефакторинга по одному за раз, компилируя и тестируя программу перед следующим видом рефакторинга.
Составьте список действий, которые вы собираетесь предпринять Ес#
тественным расширением Процесса Программирования с Псевдокодом является составление списка видов рефакторинга, которые приведут вас из точки А в точ#
ку Б. Составление такого списка поможет поддерживать каждое изменение в со#
ответствующем контексте.
Составьте и поддерживайте список видов рефакторинга, которые сле'
дует выполнить позже Выполняя один вид рефакторинга, вы можете счесть необходимым еще один вид. Взявшись за него, вы можете обнаружить в коде не#
достатки, призывающие к третьему виду. Если изменение не требуется сию мину#
ту, включите его в список изменений, которые следовало бы внести в программу когда#то, но не обязательно вносить прямо сейчас.
Часто создавайте контрольные точки Иногда рефакторинг внезапно ухо#
дит в сторону, поэтому вам следует не только сохранять первоначальный код, но и создавать контрольные точки на разных этапах рефакторинга, чтобы можно было вернуться к работоспособной программе, если рефакторинг заведет вас в тупик.
Используйте предупреждения компилятора Компилятор часто не замеча#
ет небольших ошибок. Задав компилятору самый строгий уровень диагностики,
вы сможете исправлять многие ошибки почти сразу после их внесения.
Выполняйте регрессивное тестирование Дополните обзоры измененного кода регрессивным тестированием. Конечно, это зависит от наличия хорошего набора тестов (см. главу 22).
Вмешательство в рабочую сис- тему больше похоже на вскры- тие головного мозга и замену нерва, чем на замену проклад- ки в кране. Облегчилось ли бы сопровождение программ, если б оно называлось «нейрохирур- гией ПО»?
Джеральд Вайнберг
(Gerald Weinberg)

ГЛАВА 24 Рефакторинг
567
Создавайте дополнительные тесты Не ограничивайтесь регрессивным тес#
тированием программы с использованием старых тестов — создавайте новые блочные тесты для проверки нового кода. Тесты, устаревшие в результате рефак#
торинга, удаляйте.
Выполняйте обзоры изменений Если обзоры важны при первоначальном написании кода, то при последующих изме#
нениях их важность лишь повышается. Эд Йордон сообщает,
что первая попытка внесения изменения в код более чем в половине случаев оказы#
вается ошибочной (Yourdon, 1986b). Интересно, что, если программисты имеют дело не с несколькими строками кода, а с более объемным фрагментом, вероятность вне#
сения корректного изменения более высока (рис. 24#1). Точнее говоря, по мере уве#
личения числа изменяемых строк с одной до пяти вероятность внесения непра#
вильного изменения повышается, а после этого снижается.
Рис. 24'1. Небольшие изменения чаще оказываются ошибочными,
чем более крупные (Weinberg, 1983)
Программисты относятся к небольшим изменениям легкомысленно. Они не ана#
лизируют их, не просят коллег выполнить их обзор, а иногда даже не запускают программу, чтобы проверить, что исправление корректно.
Мораль проста: рассматривайте простые изменения так, как если бы они были сложными. В одной организации, в которой были введены обзоры изменений одной строки кода, было обнаружено, что доля ошибочных изменений снизилась с 55% до 2% (Freedman and Weinberg, 1982). В другой орга#
низации, работающей в сфере телекоммуникаций, введение обзоров изменений позволило повысить их корректность с 86% до 99,6% (Perrott, 2004).
Изменяйте подход в зависимости от рискованности рефакторинга Не#
которые виды рефакторинга сопряжены с более высоким риском, чем другие. Так,
«замена магического числа на именованную константу» относительно безопасна.
Виды рефакторинга, предполагающие изменение интерфейса класса или метода,
схемы БД или булевых тестов, обычно более рискованны. Если рефакторинг не#
сложен, вы можете оптимизировать процесс рефакторинга, выполняя более од#
ного вида за раз и ограничиваясь регрессивным тестированием кода без его офи#
циального обзора.
Перекрестная ссылка Об обзо- рах см. главу 21.

568
ЧАСТЬ V Усовершенствование кода
В случае более рискованных видов рефакторинга будьте более осторожны. Вы#
полняйте их по одному за раз. Попросите кого#то провести обзор изменения или выполните рефакторинг в паре, дополнив этим обычную проверку кода средствами компилятора и блочное тестирование.
Плохие причины выполнения рефакторинга
Несмотря на всю свою эффективность, рефакторинг — не панацея и допускает несколько специфических видов злоупотребления.
Не рассматривайте рефакторинг как оправдание на'
писания плохого кода с намерением исправить его
позднее Самой большой проблемой с рефакторингом яв#
ляется его неверное применение. Иногда программисты говорят, что они выполняют рефакторинг, хотя на самом деле они просто пробуют что попало в надежде хоть как#
то привести код в работоспособное состояние. Рефакторинг — это
изменение
работоспособного кода, не влияющее на поведение программы. Программисты,
которые возятся с плохим кодом, не выполняют рефакторинг — они занимаются хакерством.
Не рассматривайте рефакторинг как способ, позво'
ляющий избежать переписывания кода Иногда код невозможно улучшить небольшими изменениями — его нуж#
но выбросить и переписать с нуля. Если вы выполняете круп#
номасштабный рефакторинг, спросите себя, не следует ли вместо этого перепро#
ектировать и переписать фрагмент кода.
24.5. Стратегии рефакторинга
Число видов рефакторинга, выгодных для любой конкретной программы, прак#
тически бесконечно. Рефакторинг подчиняется тому же закону снижения выго#
ды, что и другие процессы программирования, и к нему также относится прави#
ло 80/20. Тратьте время на 20% видов рефакторинга, обеспечивающих 80% выго#
ды. При определении наиболее важных видов рефакторинга учитывайте следую#
щие советы.
Выполняйте рефакторинг при создании новых методов Создавая метод,
проверьте, хорошо ли организованы связанные с ним методы. При необходимо#
сти выполните их рефакторинг.
Выполняйте рефакторинг при создании новых классов Создание нового класса часто подчеркивает недостатки имеющегося кода. Используйте эту возмож#
ность для рефакторинга других классов, тесно взаимодействующих с новым классом.
Выполняйте рефакторинг при исправлении дефектов Используйте знания,
полученные при исправлении ошибки, для улучшения других фрагментов кода,
которые могут быть подвержены похожим ошибкам.
Выполняйте рефакторинг модулей, подверженных ошибкам Некоторые модули более подвержены ошибкам, чем другие. Есть ли в коде фрагмент, внуша#
ющий страх вам и всем остальным членам вашей группы? Скорее всего он под#
Не реализуйте какую-то воз- можность частично, намерева- ясь позднее завершить ее в процессе рефакторинга.
Джон Манзо (John Manzo)
Крупномасштабный рефакто- ринг — путь к катастрофе.
Кент Бек (Kent Beck)

ГЛАВА 24 Рефакторинг
569
вержен ошибкам. Большинство людей естественным образом стараются избегать таких проблемных фрагментов кода,
однако вы добьетесь большего успеха, если будете рассмат#
ривать их как мишени рефакторинга (Jones, 2000).
Выполняйте рефакторинг сложных модулей Другим подходом к рефакторингу является концентрация на моду#
лях, имеющих максимальные оценки сложности (см. подраздел «Как измерить слож#
ность» раздела 19.6). Одно классическое исследование показало, что использова#
ние этой методики при сопровождении программ приводило к существенному повышению их качества (Henry and Kafura, 1984).
При сопровождении программы улучшайте фрагменты, к которым при'
касаетесь Код, который никогда не изменяется, не требует рефакторинга. Но если вы все же изменяете фрагмент кода, убедитесь, что вы оставляете его в луч#
шем состоянии, чем обнаружили.
Определите интерфейс между аккуратным и безобразным кодом и пере'
местите безобразный код на другую сторону этого интерфейса «Реаль#
ность» часто оказывается грязнее, чем нам хотелось бы. Эта грязь может быть обусловлена сложными бизнес#правилами, интерфейсами с оборудованием, про#
граммными интерфейсами и т. д. При работе со старыми системами часто при#
ходится иметь дело с плохим кодом, который тем не менее должен постоянно оставаться работоспособным.
Эффективной стратегией омоложения внедренных старых систем является опре#
деление фрагментов, относящихся к грязному реальному миру, фрагментов, фор#
мирующих идеализированный новый мир, и фрагментов, определяющих интер#
фейс между двумя мирами (рис. 24#2).
Рис. 24'2. Ваш код не обязан быть грязным только потому, что таков реальный
мир. Рассматривайте систему как комбинацию идеального кода, грязного кода
и интерфейса между идеальным и грязным кодом
Перекрестная ссылка О коде,
подверженном ошибкам, см.
подраздел «Какие классы со- держат наибольшее число оши- бок?» раздела 22.4.

570
ЧАСТЬ V Усовершенствование кода
Работая с системой, вы можете постепенно перемещать грязный код через «интер#
фейс между двумя мирами» в более организованный идеальный мир. Если вы толь#
ко начинаете работать с унаследованной системой, она может почти полностью относиться к грязному миру. Одна эффективная политика подразумевает, что каж#
дый раз, когда вы прикасаетесь к какому#то фрагменту грязного кода, вы должны привести его в соответствие с текущими стандартами кодирования, присвоить пе#
ременным ясные имена и т. д. — иначе говоря, переместить его в идеальный мир.
Со временем это может обеспечить быстрое улучшение базы кода (рис. 24#3).
Рис. 24'3. Одной из стратегий улучшения готовой системы является постепенный
рефакторинг плохого унаследованного кода — перемещение его на другую сторону
«интерфейса между двумя мирами»
Контрольный список: безопасный рефакторинг
 Является ли каждое изменение частью систематичной стратегии изменений?
 Сохранили ли вы первоначальный код перед началом рефакторинга?
 Стараетесь ли вы ограничить объем каждого вида рефакторинга?
 Выполняете ли вы отдельные виды рефакторинга по одному за раз?
 Составили ли вы список действий, которые собираетесь предпринять во время рефакторинга?
 Ведете ли вы список видов рефакторинга, которые следовало бы выпол- нить позднее?
 Выполняете ли вы регрессивное тестирование после каждого вида рефак- торинга?
 Выполняете ли вы обзор сложных изменений и изменений, влияющих на критически важный код?
 Рассматриваете ли вы рискованность отдельных видов рефакторинга и адап- тируете ли вы свой подход соответствующим образом?
 Убеждаетесь ли вы, что изменения улучшают внутреннее качество программы,
а не ухудшают его?
 Не рассматриваете ли вы рефакторинг как оправдание написания плохого кода или как способ избежать переписывания плохого кода?
http://cc2e.com/2457

1   ...   67   68   69   70   71   72   73   74   ...   106


написать администратору сайта