использовать стандартные названия функций и переменных. Это позволяет не тратить слишком много времени на придумывание имён и улучшает воспоиятие кода. Даже если у вас будут свои собственные стандарты, которые кроме вас никто не соблюдают, постороннему человеку будет всё же проще разобраться за счёт того, что разобравшись с одной функцией, он сможет обоснованно предположить о назначении другой, благодаря схожему названию.
Какие можно привести примеры:
— практически все называют переменные цикла буквами i, j, k, для циклов первой, второй и третьей вложенности соответственно;
— какие–то временные, короткоживущие переменные часто снабжают префиксом «temp» или «tmp», например, tempFileName для файла, который вскоре удалится;
— логические переменные становятся куда нагляднее с префиксами типа is, has, can, например, isButtonVisible, canAddPoint, hasPoint;
— в случае коптрования данных из одной переменной в другую, лучше использовать префиксы source и dest, а не суффиксы 1 и 2, «copy(array1, array2)» выглядит менее понятно, чем «copy(sourceArray, destArray)».
Примеры можно приводить бесконечно, понимание таких вещей обычно приходит с опытом. Важно то, что если у функции или переменной есть какая–то особенность, которую можно подчеркнуть слегка поменяв название — почему не сделать это. Когда читаешь код — важна каждая мелочь. Это как в детективе — любой штрих может привести к разгадке. Только в отличии от преступников, программисты обычно делают что–то полезное или на худой конец безвредное, и большое количество деталей тут только в плюс.
Преобразование одной большой функции в две маленькие
Сейчас мы добрались до куда более сложной и куда менее однозначной темы. Дело в том, что человеческое восприятие так устроено, что анализировать сразу большой объём информации ему крайне сложно. Именно по этому, книги принято разбивать на главы, а сами главы на абзацы.
В программировании всё обстоит немного иначе и это может быть причиной путаницы. Тут текст разбит на функции (процедуры, методы, классы, сейчас это не так важно), а основное назначение функций — это возможность их повторного использования. То есть по сути, с их помощью суммарный объём кода уменьшается, они позволяют избежать многократного повторения одних и тех же фрагментов кода и тому подобное.
Использование функций как инструмента для улучшения читабельности кода, на мой взгляд, сильно недооценёно. Как показывает практика, небольшое усложнение кода ухудшает его восприятие значительно.
type
TRect = record
Left: Integer;
Right: Integer;
Top: Integer;
Bottom: Integer;
end;
function RectsLength(Rects: array of TRect): Integer;
var
I: Integer;
Width, Height: Integer;
begin
Result:= 0;
for I:= 0 to Length(Rects) — 1 do
begin
Width:= Rects[I].Right — Rects[I].Left;
Height:= Rects[I].Bottom — Rects[I].Top;
Result:= Result + 2 * Width + 2 * Height;
end;
end;
Выше приведён простой пример, который рассчитывает сумму периметров прямоугольников в массиве. Пока он не выглядит сильно сложным, но, предположим, что задача немного изменилась и нам сказали не учитывать прямоугольники площадью меньше некоего числа MinLength.
function RectsLength(Rects: array of TRect; MinLength: Integer): Integer;
var
I: Integer;
Width, Height, Len: Integer;
begin
Result:= 0;
for I:= 0 to Length(Rects) — 1 do
begin
Width:= Rects[I].Right — Rects[I].Left;
Height:= Rects[I].Bottom — Rects[I].Top;
Len:= 2 * Width + 2 * Height;
if Len >= MinLength then
Result:= Result + Len;
end;
end;
Не скажу, что намного сложнее, но взгляд спотыкается. А если бы мы сразу выделили функцию RectLenght, считающую площадь отдельного прямоугольника вышло бы несколько проще:
function RectLength(Rect: TRect): Integer;
var
Width, Height: Integer;
begin
Width:= Rect. Right — Rect. Left;
Height:= Rect. Bottom — Rect. Top;
Result:= 2 * Width + 2 * Height;
end;
function RectsLength(Rects: array of TRect; MinLength: Integer): Integer;
var
I: Integer;
Len: Integer;
begin
Result:= 0;
for I:= 0 to Length(Rects) — 1 do
begin
Len:= RectLength(Rects[I]);
if Len >= MinLength then
Result:= Result + Len;
end;
end;
И пусть вас не смущает, что в сумме кода стало немного больше. Мне ещё ни разу не приходилось жалеть о такого рода рефакторинге. То есть были, конечно, случаи, когда он был не оправдан и только путал… У каждого бывали ошибки. Но никогда проблемы не были связаны с увеличением суммарного объема кода. Иногда, даже если вы смогли разделить функцию на две функции того же размера (суммарное увеличение кода в два раза), но, при этом хорошо разделили их логически — это бывает оправдано.
Я не хочу сказать, что разбить большую функцию на две или несколько маленьких можно и нужно всегда. Принять решение об этом — задача, в ряде случаев, очень сложная. Поэтому, я хотел бы посвятить следующую главу признакам необъодимости такого рефакторинга.
Признаки необходимости выделения функции
Как я уже написал выше — тема довольно сложная и неоднозначная. Подобных признаков может быть сколько угодно, у каждого они свои, но какие–то общие рекомендации на основании своего опыта я постараюсь дать.
1. Размер функции.
Последние комментарии
11 часов 19 минут назад
11 часов 38 минут назад
11 часов 46 минут назад
11 часов 48 минут назад
11 часов 51 минут назад
12 часов 8 минут назад