— Возрастает вероятность повторного использования кода. Если вы один раз вычислили периметр квадрата внутри функции, то, вполне вероятно, что и в следующий раз вы не вынесете её наружу. В результате, вполне возможно, что одинаковый фрагмент кода будет встречаться у вас многократно. Это само по себе не очень хорошо и может значительно увеличить суммарный объём кода, но, если в этом коде ещё и допущена ошибка или его нужно поменять по какой–то другой причине — можно наткнуться на серьёзные, долгоживущие проблемы. Совершенно типичная ситуация — поменяли в одном месте, не поменяли в другом. Где–то в третьем месте поменяли, но иначе. В результате код расползается, происходит рассинхронихация и прочие весьма неприятные вещи.
— Код станет проще читать.
Следует отметить, что код выполняющий отдельное осмысленное действие в общем случае не обязан идти подряд.
>function RectsLength(Rects: array of TRect; MinLength: Integer): Integer;
>var
>I: Integer;
>Len: Integer;
>Widths, Heights: array of Integer;
>begin
>Result:= 0;
>SetLength(Widths, Length(Rects));
>SetLength(Heights, Length(Rects));
>for I:= 0 to Length(Rects) — 1 do
>begin
>Widths[I]:= Rects[I].Right — Rects[I].Left;
>Heights[I]:= Rects[I].Bottom — Rects[I].Top;
>end;
>for I:= 0 to Length(Rects) — 1 do
>begin
> Len:= 2 * Widths[I] + 2 * Heights[I];
>if Len >= MinLength then
> Result:= Result + Len;
>end;
end;
Это та же самая функция расчёта суммы периметров из прошлой главы. Мы уже видели несколько вариантов её реализации, но этот, пожалуй, наиболее сложный и избыточный. Понятно, что тут легко избавится от второго цикла, что значительно упростит конструкцию, но ведь между циклами может быть ещё много другого кода. Тогда всё станет куда менее очевидно. В этом случае, знание того, что для расчёта суммы периметров прямоугольников, надо так или иначе рассчитать периметр каждого из них, может сослужить хорошую службу.
5. Сложные условия. Логические выражения по праву занимают одно из лидирующих мест по сложности восприятия. Именно по этому, по возможности, их следует выделять в отдельные функции. Единственный совет, при этом — старайтесь избегать отрицаний в названиях функций.
>procedure AddPointToRect(x, y: Integer; Rect: TRect);
>begin
>if (x >= Rect. Left) and (x <= Rect. Right) and (y >= Rect. Top) and (y <= Rect. Bottom) then
>AddPoint(x, y);
>end;
>Лучше заменить на:
>function PointOnRect(x, y: Integer; Rect: TRect): Boolean;
>begin
>Result:= (x >= Rect. Left) and (x <= Rect. Right) and (y >= Rect. Top) and (y <= Rect. Bottom);
>end;
>procedure AddPointToRect(x, y: Integer; Rect: TRect);
>begin
>if PointOnRect(x, y, Rect) then
>AddPoint(x, y);
>end;
Однако для функции PointOutsideRect, добавляющей точку за пределами прямоугольника, лучше не писать «if PointOutsideRect(x, y, Rect) then», а написать «if not PointOnRect(x, y, Rect) then».
6. Высокий уровень вложенности. Бывает функция как матрёшка. Блок кода, в нём ещё блок кода, в нём ещё и так далее. Читать это также довольно трудно. Пример приводить не буду, чтобы не захламлять текст, отмечу лишь, что блок целиком (текст между скобками «begin end» или «{}» для C-подобных языков) очень часто легко переносится в отдельную функцию.
Выделение функции в процессе написания
Рекомендации прошлой главы хороши, когда вы смотрите ранее написанный или чужой код. Тогда да, чтобы разобраться в том, что написано — помогает разбить крупные функции на более мелкие.
Согласитесь, если бы код сразу был написан в виде небольших, понятных, осмысленных функций — многих проблем можно было бы избежать.
Как же писать «короткими фразами»? Разумеется, в первую очередь, это дело привычки. Не думаю, что мне будет по силам формализовать этот процесс, я лишь попробую передать свои ощущения о том, как можно себе помочь.
1. Попробуйте проговаривать то, что должен делать ваш код. При этом, первое время, может потребоваться вдумываться в свои слова более внимательно.
Например, вы говорите себе: «если точка внутри прямоугольника, то» и при этом пишите: «if (x >= Rect. Left) and (x <= Rect. Right) and (y >= Rect. Top) and (y <= Rect. Bottom) then». Кто мешает вам написать сразу «if PointOnRect(x, y, Rect) then»? И не важно, что у вас пока нет функции PointOnRect, вы её легко напишите следующим шагом. А если даже забудите — комптлятор вам подскажет.