DarkGoodWIN
Рефакторинг
Зачем?
Введение
Время от времени мне доводилось отвечать на вопросы, которые в конечном счёте можно сформулировать как: «что такое рефакторинг и для чего он нужен?». Конкретные вопросы могли быть разные, например, чем объектно–ориентированное программирование лучше функционального. Этот вопрос относится к рефакторингу куда больше, чем принято считать.
Так что же такое рефакторинг? Это любое изменение программы, которое не изменяет её функциональности. Для чего же тогда делать это изменение? Дело в том, что в отличии от книг, где буковки прочно занимают свои места раз и навсегда, программный код — это нечто живое, постоянно изменяемое. В крупных проектах объём кода может составлять десятки или даже сотни мегабайт. Это сопоставимо по объему с домашней библиотекой средних размеров. Найти что–то среди такого количества информации — задача, вообще говоря, нетривиальная, ну а понять написанное — зачастую и вовсе неразрешимая. Так вот, рефакторинг направлен, прежде всего, на решение этих проблем.
Задачи рефакторинга тесно связанны с задачами написания понятного, удобного кода. Соответственно, если я пишу как следует писать или чего лучше избегать — это к рефакторингу не относится. С одной стороны. Но ведь следуя этим рекомендациям, вы можете пересмотреть свой код и исправить потенциальные ошибки. А вот это уже чистой воды рефакторинг. Поэтому я не буду особенно зацикливаться именно на рефакторинге, а буду рассказывать о хорошем, понятном коде.
Разумеется, моё представление о хорошем коде может коренным образом отличаться от вашего и тут я не претендую на статус непогрешимого. Хочу только заметить, что всё, что изложено в этой книге основано на многолетних наблюдениях и в целом, так или иначе, согласуется с общепризнанными подходами к программированию.
Примеры я буду приводить на языке Object Pascal. В основном я работаю на нём, пишу на Delphi. Предвидя нападки со стороны поклонников C-подобных языков, скажу два тезиса:
1. Подходы к организации программного кода от языка не зависят. Язык может лишь поддерживать или не поддерживать те или иные конструкции.
2. Сугубо субъективно считаю Pascal более читабельным языком. То есть для программирования, возможно, большая локаничность C — плюс, а для обучения, я считаю, лучше годиться более развернутый, более приближенный к человеческой речи синтаксис Pascal.
Ну что же, начнём, пожалуй.
Именование переменных и функций
Первым делом проиллюстрируем то, о чем буду говорить примером:
function func(i1, i2: Integer): Integer;
begin
Result:= i1 * i2;
end;
Довольно простая функция, но сколько времени нужно, чтобы понять что она делает? А теперь представьте, что она куда больше по размеру и у вас таких десяток? Каждый раз, когда вы или кто–то ещё будет натыкаться на такую функцию — неменуемо будет тратиться лишнее время.
Приведу два основных тезиса:
1. Если по названию функции понятно, что она делает — есть высокая вероятность, что вы будете избавлены от необходимости анализа её содержимого.
2. Если названия переменных отражают характер величины, которая в них хранится — код читать значительно проще, а значит быстрее и безопаснее. Неправильно истолкованный фрагмент кода может привести к серьёзной ошибке.
Попробуем немного поменять нашу функцию:
function RectArea(Width, Height: Integer): Integer;
begin
Result:= Width * Height;
end;
Правда стало понятнее? Вообще говоря, исходя из того, что функция просто умножает два числа, можно было бы её просто Mult. Однако, в ряде случаев это хуже. Посмотрим на такой фрагмент кода:
if Mult(Width, Height) > 2 then
WriteLn('Big rectangel')
else
WriteLn('Small rectangel');
И сравним его с таким:
if RectArea(Width, Height) > 2 then
WriteLn('Big rectangel')
else
WriteLn('Small rectangel');
На мой взгляд, последний читается проще. Всё–таки есть разница: «если ширина, умноженная на высоту больше двух, пишем «большой прямоугольник», иначе пишем «маленький прямоугольник» или «если площадь прямоугольника больше двух, пишем «большой прямоугольник», иначе пишем «маленький прямоугольник».
Забегая вперёд, напишу, что для улучшения читабельности, чтобы при этом избежать дублирования кода, иногда делают что–то вроде этого: