Антипаттерн №1 — Магическое число (Magic Number)

18 апреля, 2018DEV

Из всего многообразия грехов разработки магическое число (magic number) — это, пожалуй, самый распространённый антипаттерн. Греховность его можно оценить как лёгкую, т.к., в целом, данный методологический изъян лёгок в обнаружении и устранении.

Магическое число — это оперирование явно указанными в коде коэффициентами (как правило целочисленными), значение и смысл которых знает только автор программы. Как правило, подобные волшебные числа встречаются в виде параметров, передаваемых при вызове методов\функций\процедур или же при присвоении каких-либо свойств\полей. Обязательно — без поясняющих комментариев. Изменение подобных коэффициентов порой несут абсолютно непредсказуемый характер и именно поэтому данный антипаттерн называется магическим числом 🙂 Возьмём канонический пример:

this.Draw(2, 320, 240, 3, 7);

Понятно, что это какая-то рисовалка и больше, собственно, ничего не понятно. И если с 320 и 240 ещё можно как-то разобраться догадаться, то что такое 2, 3 и 7 — совершенно не ясно. Это могут быть номера рисуемых фигур, заданное количество отступа, ширина линий, настройки кистей или абсолютно что-либо другое (не обязательно даже связанное с рисованием действие).

Пример с методом, который носит имя Draw на самом деле упрощён и даёт хоть какое-то поле для понимания кода: здесь однозначный нейминг и знакомые цифры 320×240 наталкивают на догадки. А вот что делать со следующим кодом:

this.Upload(1, 6);

Что есть 1? Что будет если туда записать 2 или 3? Что такое 6? Любое изменение может не изменить поведение совсем, а может и кардинально отличаться. Любые попытки дёргать такой метод вслепую делаются на свой страх и риск. Ведь эти магические числа могут быть номером файла, номером сервера, прокси, абсолютно чем угодно. Эти значение как-то участвуют в логике, но мы, к сожалению, об этом ничего знаем.

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

Как исправить? Вводить однозначные константы или же списки Enum (с ними нужно быть тоже очень осторожными, т.к. есть некоторые ньюансы при использовании в больших командах, при поддержании обратной связи и т.д.):

private const int WINDOW_WIDTH=320;
private const int WINDOW_HEIGHT=240;
private const int PADDING_LEFT=3;
private const int PADDING_RIGHT=7;
private const int BORDER_WIDTH=2;

this.Draw(BORDER_WIDTH, WINDOW_WIDTH, WINDOW_HEIGHT, PADDING_LEFT, PADDING_RIGHT);

Или же пример с списком Enum:

private enum Servers : uint
{
    EUROPE = 1,
    USA_CANADA = 2,
};

private enum Files : uint
{
    README = 1,
    CONTRACT = 2,
    INVOICE1 = 3,
    INVOICE2 = 4,
    PASSPORT = 5,
    PHOTO = 6,
};

this.Upload(Servers.EUROPE, Files.PHOTO);

В целом, конечно, магические числа не очень страшны (если так вообще можно говорить в контексте антипаттернов). Более того, уверен, написание подобного кода с запашком был у каждого в копилке личного опыта и это нормально. Плохо, когда мы наступая на эти грабли не делаем выводов. Не становимся лучше, не исправляем ошибок и не развиваемся. Вот где, действительно, страшно.

По-меньше вам магических чисел в коде, братцы.

Leave a comment

Ваш адрес email не будет опубликован.

Prev Post Next Post