Как стать автором
Обновить

Роберт, ты мне не дядюшка

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров28K

Роберт Мартин нехило так повлиял на айти‑индустрию. Он придумал принципы 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. Подтверждений у Мартина никаких нет. Всё учение о чистом коде строится лишь на вере.

Вообще, Роберт Мартин в своей книге делает очень много заявлений и лозунгов, иллюстрирует их отвратительными примерами кода и не приводит никаких подтверждений. Этим книга "Чистый код" кардинально отличается от "Совершенного кода" Макконнелла. В "Совершенном коде" нет лозунгов, зато есть ссылки на научные работы и комплексный взгляд на разработку ПО. Да, книга Макконнелла старенькая, но если хочется что-то почитать про написание понятного и поддерживаемого кода, то лучше взять в руки именно её, а не пропагандистскую агитку от "дядюшки Боба", которая точно испортит вашу кодовую базу.

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

Теги:
Хабы:
Всего голосов 142: ↑119 и ↓23+116
Комментарии125

Публикации

Ближайшие события