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

Руководство по стилю программирования и конструированию по


Скачать 7.6 Mb.
НазваниеРуководство по стилю программирования и конструированию по
Дата18.05.2023
Размер7.6 Mb.
Формат файлаpdf
Имя файлаCode_Complete.pdf
ТипРуководство
#1139697
страница70 из 104
1   ...   66   67   68   69   70   71   72   73   ...   104
ГЛАВА 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

ГЛАВА 24 Рефакторинг
571
Дополнительные ресурсы
Процесс рефакторинга имеет много общего с процессом устранения дефектов (см. раздел 23.3). Факторы риска, свя- занные с рефакторингом, похожи на факторы риска, каса- ющиеся оптимизации кода. Об управлении факторами риска при оптимизации кода см. раздел 25.6.
Fowler, Martin.
Refactoring: Improving the Design of Existing Code. Reading, MA: Add- ison Wesley, 1999. Это самое полное и подробное руководство по рефакторингу содержит детальное обсуждение многих конкретных видов рефакторинга, упомя- нутых в этой главе, а также других видов, которых я не касался. Фаулер привел много примеров пошагового выполнения каждого вида рефакторинга.
Ключевые моменты

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

Изменения могут приводить как к улучшению, так и к ухудшению ПО. Главное
Правило Эволюции ПО заключается в том, что при эволюции кода внутрен- нее качество программы должно повышаться.

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

Другое условие — изучение многих конкретных видов рефакторинга.

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

Рефакторинг во время разработки — самая благоприятная возможность улуч- шения программы и внесения в нее всех изменений, которые вам так или иначе захочется внести позднее. Используйте эту возможность!
http://cc2e.com/2464

572
ЧАСТЬ V Усовершенствование кода
Г Л А В А 2 5
Стратегии
оптимизации кода
Содержание

25.1. Общее обсуждение производительности ПО

25.2. Введение в оптимизацию кода

25.3. Где искать жир и патоку?

25.4. Оценка производительности

25.5. Итерация

25.6. Подход к оптимизации кода: резюме
Связанные темы

Методики оптимизации кода: глава 26

Архитектура ПО: раздел 3.5
В этой главе обсуждается исторически противоречивая проблема — повышение производительности кода. В 1960-х годах ресурсы компьютеров были крайне ограниченны, поэтому эффективность их использования была вопросом перво- степенной важности. По мере роста производительности компьютеров в 70-х программисты начали понимать, насколько упор на производительность вредит удобочитаемости и легкости сопровождения кода, и оптимизация кода отошла на задний план. Вместе с микрокомпьютерной революцией, свидетелями которой мы стали в 80-х, проблема эффективного использования ресурсов вернулась, но в 90-х ее важность постепенно уменьшилась. В 2000-х мы опять столкнулись с этой про- блемой, только теперь она связана с ограниченной памятью мобильных телефо- нов, карманных ПК и подобных устройств, а также со временем выполнения ин- терпретируемого кода.
Проблемы с производительностью можно решать на двух уровнях: стратегическом и тактическом. В этой главе рассматриваются стратегические вопросы произво- дительности: мы выясним, что такое производительность, насколько она важна,
и обсудим общий подход к ее повышению. Если стратегии достижения нужной производительности вам уже хорошо известны и вы хотите узнать конкретные http://cc2e.com/2578

ГЛАВА 25 Стратегии оптимизации кода
573
методики ее повышения на уровне кода, можете перейти к главе 26. Однако прежде чем приступать к серьезной работе над повышением производительности, хотя бы просмотрите эту главу, чтобы не потратить время на оптимизацию кода тог- да, когда следовало бы заняться чем-то другим.
25.1. Общее обсуждение производительности ПО
Оптимизация кода — лишь один из способов повышения производительности программы. Часто можно найти другие способы, обеспечивающие большее повы- шение производительности за меньшее время и с меньшим вредом для кода.
Характеристики качества
и производительность
Некоторые люди смотрят на мир через розовые очки. Про- граммисты склонны воспринимать мир через кодовые очки.
Мы полагаем, что чем лучше будет наш код, тем сильнее наше
ПО понравится клиентам.
Эта точка зрения верна лишь отчасти. Пользователей боль- ше интересуют явные характеристики программы, а не ка- чество кода. Производительность обычно привлекает их внимание, только когда она сказывается на их работе. Боль- шее значение для пользователей имеет не грубая производительность приложе- ния, а объем информации, который оно позволяет обработать за конкретный срок,
а также такие факторы, как своевременное получение программы, ясный пользо- вательский интерфейс и предотвращение простоев.
Приведу пример. Я делаю цифровой камерой минимум 50 снимков в неделю. Чтобы скопировать снимки на компьютер при помощи ПО, поставляемого с камерой, я должен выбрать каждый снимок по очереди, причем в окне программы отобра- жаются только 6 снимков сразу. В результате копирование 50 изображений пре- вращается в долгий и нудный процесс, требующий десятков щелчков и массы переключений между окнами. Устав от всего этого, я купил карту памяти, подклю- чаемую прямо к компьютеру и воспринимаемую им как диск. Теперь для копиро- вания изображений на диск компьютера я могу использовать Проводник Windows.
Я делаю пару щелчков, нажимаю Ctrl+A и перетаскиваю все файлы в нужное мес- то. Меня не волнует, передает ли карта памяти каждый файл вдвое медленнее или быстрее, чем другое ПО, потому что сейчас я могу обработать больший объем информации за меньшее время. Каким бы быстрым или медленным ни был код драйвера карты памяти, его производительность выше.
Производительность только косвенно связана с быстродействием кода.
Чем больше вы работаете над скоростью кода, тем меньше внимания уделяете другим характеристикам его качества. Не приносите их в жер- тву быстродействию. Стремление к повышению быстродействия может снизить общую производительность программы, а не повысить ее.
Во имя эффективности — при- чем достигается она далеко не всегда — совершается больше компьютерных грехов, чем по любой другой причине, включая банальную глупость.
У. А. Вульф (W. A. Wulf)

574
ЧАСТЬ V Усовершенствование кода
Производительность и оптимизация кода
Решив, что эффективность кода — будь то его быстродействие или объем — дол- жна быть приоритетом, не торопитесь улучшать быстродействие или объем на уровне кода, а рассмотрите несколько вариантов. Подумайте об эффективности в контексте:

требований к программе;

проекта программы;

проектов классов и методов;

взаимодействия программы с ОС;

компиляции кода;

оборудования;

оптимизации кода.
Требования к программе
Высокая производительность считается требованием гораздо чаще, чем на самом деле им является. Барри Бом рассказывает, что при разработке одной системы в компании TRW сначала решили, что время реакции системы не должно превы- шать 1 секунды. Это требование привело к разработке очень сложного проекта,
на реализацию которого пришлось бы потратить примерно 100 млн долларов. Даль- нейший анализ показал, что в 90% случаев пользователей устроит время реакции,
равное 4 секундам. Изменение требования позволило снизить общую стоимость системы примерно на 70 млн долларов (Boehm, 2000b).
Прежде чем тратить время на решение проблемы с производительностью, убеди- тесь, что она действительно требует решения.
Проект программы
Проект программы определяет ее основные черты — глав- ным образом, способ ее разделения на классы. Проект мо- жет как облегчить, так и затруднить создание высокопро- изводительной системы.
Например, при высокоуровневом проектировании одной реальной программы сбора и обработки данных в качестве ключевого атрибута была определена пропускная способность обработки резуль- татов измерений. Каждое измерение включало определение значения электриче- ской величины, калибровку значения, масштабирование значения и преобразо- вание исходных единиц измерения (таких как милливольты) в единицы прикладной области (такие как градусы Цельсия).
Если бы при высокоуровневом проектировании программисты не оценили все факторы риска, им в итоге пришлось бы оптимизировать алгоритмы вычисления многочленов 13 степени, т. е. многочленов, содержащих 14 переменных с макси- мальной степенью 13. Вместо этого они решили проблему, выбрав другое обору- дование и разработав высокоуровневый проект, предусматривающий использо- вание десятков многочленов 3 степени. Оптимизировав код, они вряд ли получи- ли бы нужные результаты. Это пример проблемы, которую нужно было решать на уровне проектирования.
Перекрестная ссылка Проекти- рование высокопроизводитель- ных программ рассматривается в работах, указанных в разде- ле «Дополнительные ресурсы»
в конце главы.

1   ...   66   67   68   69   70   71   72   73   ...   104


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