
Роберт Мартин нехило так повлиял на айти‑индустрию. Он придумал принципы 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. Подтверждений у Мартина никаких нет. Всё учение о чистом коде строится лишь на вере.
Вообще, Роберт Мартин в своей книге делает очень много заявлений и лозунгов, иллюстрирует их отвратительными примерами кода и не приводит никаких подтверждений. Этим книга "Чистый код" кардинально отличается от "Совершенного кода" Макконнелла. В "Совершенном коде" нет лозунгов, зато есть ссылки на научные работы и комплексный взгляд на разработку ПО. Да, книга Макконнелла старенькая, но если хочется что-то почитать про написание понятного и поддерживаемого кода, то лучше взять в руки именно её, а не пропагандистскую агитку от "дядюшки Боба", которая точно испортит вашу кодовую базу.
Я привел всего два листинга. Но если этих двух примеров недостаточно, чтобы перестать верить в чистый код и пророка его "дядюшку Боба", то значит вера ваша слишком крепка, и я перед ней бессилен. Ничего не поделать. Возможно, наступит момент, когда вы тоже возьмёте в руки свою печатную копию учения о чистом коде, откроете листинги, внимательно их почитаете и ужаснётесь. В этот момент вам откроется истина. Просто сейчас вы к ней не готовы.