Роберт, ты мне не дядюшка
Роберт Мартин нехило так повлиял на айти‑индустрию. Он придумал принципы SOLID, о которых спрашивают на собесах, пишут статьи на Хабре и спорят в комментариях. Он написал книгу «Чистый код» и сделал это словосочетание айтишным мемом. Если зайти на хэдхантер, вбить в поиске слово «чистый», выбрать специализацию «Программист, разработчик» и нажать «Найти», получим больше семисот вакансий. Про чистоту кода и архитектуры спорят на код‑ревью, в комментариях и статьях по всему интернету. Разговоров о чистоте внутри айти‑тусовки бывает так много, словно мы находимся в сообществе клинеров, а не программистов.
Мартин называет себя «дядюшкой Бобом». В своих работах он выступает в образе опытного мудрого и взрослого родственника, который несёт свет и знания таким зелёным и неопытным племянникам. И у него отлично получилось втереться в доверие! Типичный хороший программист‑анальник бессилен перед таким добрым дядей. И я знаю, о чём пишу. Восемь лет назад я сам запоем читал книги дядюшки, а потом до усрачки защищал чистоту кода на код‑ревью. Я на себе почувствовал, насколько Роберт Мартин отличный агитатор и пропагандист. Работая с другими людьми, читая статьи и обсуждения на Хабре и хакерньюс, анализируя требования к вакансиям, я понимаю, что не я один попался на отличную пропаганду от «дядюшки Боба».
Книга «Чистый код» вышла в 2008 году. Кажется, что за это время десятки тысяч программистов должны были обжечься на советах Роберта Мартина, испоганить не одну сотню кодовых баз и сделать выводы. Но ничего подобного! «Чистый код» продолжают советовать новичкам, спрашивать о нём на собесах, использовать агитки Мартина в кодовых базах и как аргументы на код‑ревью. Пора с этим кончать. Пора развенчать культ личности «дядюшки Боба». Ведь никакой он не дядюшка. И программист тоже никакой. Сейчас я это докажу. Погнали вместе читать листинги из книги.
У меня российское издание 2016 года. Ниже я переписал листинг 10.6. В книге код на джаве, но я не джавист, поэтому повторил код один‑в-один на тайпскрипте. Это пример чистого кода от Роберта Мартина. С точки зрения Мартина, в этом листинге содержательные имена переменных, а имена функций отлично комментируют код. Шестьдесят строчек чистого кайфа и наслаждения.
class PrimeGenerator {
private static primes: number[];
private static multiplesOfPrimeFactors: number[];
static generate(n: number): number[] {
this.primes = new Array(n);
this.multiplesOfPrimeFactors = [];
this.set2AsFirstPrime();
this.checkOddNumbersForSubsequentPrimes();
return this.primes;
}
private static set2AsFirstPrime() {
this.primes[0] = 2;
this.multiplesOfPrimeFactors.push(2);
}
private static checkOddNumbersForSubsequentPrimes() {
let primeIndex = 1;
for (let candidate = 3; primeIndex < this.primes.length; candidate += 2) {
if (this.isPrime(candidate))
this.primes[primeIndex++] = candidate;
}
}
private static isPrime(candidate: number): boolean {
if (this.isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {
this.multiplesOfPrimeFactors.push(candidate);
return false;
}
return this.isNotMultipleOfAnyPreviousPrimeFactor(candidate);
}
private static isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate: number) {
let nextLargerPrimeFactor = this.primes[this.multiplesOfPrimeFactors.length];
let leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor;
return candidate === leastRelevantMultiple;
}
private static isNotMultipleOfAnyPreviousPrimeFactor(candidate: number) {
for (let n = 1; n < this.multiplesOfPrimeFactors.length; n++) {
if (this.isMultipleOfNthPrimeFactor(candidate, n))
return false;
}
return true;
}
private static isMultipleOfNthPrimeFactor(candidate: number, n: number) {
return candidate == this.smallestOddNthMultipleNotLessThanCandidate(candidate, n);
}
private static smallestOddNthMultipleNotLessThanCandidate(candidate: number, n: number) {
let multiple = this.multiplesOfPrimeFactors[n];
while (multiple < candidate) {
multiple += 2 * this.primes[n];
}
this.multiplesOfPrimeFactors[n] = multiple;
return multiple;
}
}
Этот код генерирует список из N простых чисел. Понять алгоритм, по которому он это делает, очень непросто. Если бы я попросил кандидата решить подобную задачу и в ответ получил бы такие шестьдесят строк, то кандидат не прошёл бы собеседование.
Мартин советует давать методам названия длиной в 46 символов. Пока читаешь такое название, можно забыть, что вообще происходит. Вот эта колбаса isLeastRelevantMultipleOfNextLargerPrimeFactor
похоже скорее на какой-то прикол, а не на попытки сделать код понятным для других членов команды.
Но даже если сократить эти колбасы до бэйби-сарделек, лучше не станет. Эти названия нехило так вводят в заблуждение. Не знаю, кем нужно быть, чтобы ожидать мутации данных в методе с названием isPrime
. Принимаем number на вход, на выходе даём boolean, а ещё заодно данные мутируем, но ты не переживай, всё хорошо. Эта мутация размазана ровным слоем по семи приватным методам, чтобы ты точно не решил разбираться в происходящем. Программист, которому в эту лапшу придётся вносить изменения, охренеет в голове выстраивать дата-флоу, сделает git blame, распечатает листинг на ватмане, вычислит автора по айпи и засунет этот листинг автору в анальное отверстие. Мне сложно представить другое развитие событий.
Нормальный человек решит эту задачу внутри одной функции на 19 строчек кода. Тут не нужны кучи вспомогательных методов с нечитаемыми названиями и скрытыми мутациями. Всего одна функция или статический метод.
function generatePrimes(count: number) {
const primes: number[] = [];
let primeCandidate = 2;
while (primes.length !== count) {
let candidateIsPrime = true;
for (let i = 2; i <= Math.sqrt(primeCandidate); i++) {
if (primeCandidate % i === 0) {
candidateIsPrime = false;
break;
}
}
if (candidateIsPrime) primes.push(primeCandidate);
primeCandidate += 1;
}
return primes;
}
Этот код проще, понятнее и его легче поменять. Хотя бы потому что в нём в три раза меньше строк. Чистый ли это код? Если сравнивать с кодом Мартина, то точно не чистый. Но мне плевать! В этом коде явный дата-флоу, явные мутации, он короткий и атомарный. И это главное, этого достаточно.
Давайте глянем ещё на один короткий листинг и на этом закончим. Вот пример теста прямо на джаве. Переписывать его не стал.
@Test
public void turnOnLoTempAlarmAtThreashold() throws Exception {
hw.setTemp(WAY_TOO_COLD);
controller.tic();
assertTrue(hw.heaterState());
assertTrue(hw.blowerState());
assertFalse(hw.coolerState());
assertFalse(hw.hiTempAlarm());
assertTrue(hw.loTempAlarm());
}
Нормальный тест, на мой вкус. Как и в прошлом листинге, у меня здесь вопросы к неймингу. Я бы назвал методы типа isHeaterEnabled
, но это вкусовщина. В целом, тест довольно наглядный. Но Роберту этот тест не понравился, и он отрефакторил его вот так:
@Test
public void turnOnLoTempAlarmAtThreshold() throws Exception {
wayTooCold();
assertEquals(“HBchL”, hw.getState());
}
Он называет функцию wayTooCold
более понятной. Но название этой функции ничего не говорит! Что в ней происходит, что она абстрагирует? Функция должна что-то делать, поэтому в названии она, как правило, содержит глагол. Но тут нет никакого глагола, здесь только прилагательное. Не знаю, каким гением нужно быть, чтобы, читая этот код, не проваливаться в объявление wayTooCold
. Ненужного абстрагирования Роберту мало, поэтому он решил изобрести свой доменный язык для отображения состояния системы. Большая буква -- значит true, маленькая -- false. Говорит, что в такой записи сложнее допустить ошибку, чем в прямых как палка assertTrue
и assertFalse
. Подтверждений у Мартина никаких нет. Всё учение о чистом коде строится лишь на вере.
Вообще, Роберт Мартин в своей книге делает очень много заявлений и лозунгов, иллюстрирует их отвратительными примерами кода и не приводит никаких подтверждений. Этим книга "Чистый код" кардинально отличается от "Совершенного кода" Макконнелла. В "Совершенном коде" нет лозунгов, зато есть ссылки на научные работы и комплексный взгляд на разработку ПО. Да, книга Макконнелла старенькая, но если хочется что-то почитать про написание понятного и поддерживаемого кода, то лучше взять в руки именно её, а не пропагандистскую агитку от "дядюшки Боба", которая точно испортит вашу кодовую базу.
Я привел всего два листинга. Но если этих двух примеров недостаточно, чтобы перестать верить в чистый код и пророка его "дядюшку Боба", то значит вера ваша слишком крепка, и я перед ней бессилен. Ничего не поделать. Возможно, наступит момент, когда вы тоже возьмёте в руки свою печатную копию учения о чистом коде, откроете листинги, внимательно их почитаете и ужаснётесь. В этот момент вам откроется истина. Просто сейчас вы к ней не готовы.