Дизайн — это скорее искусство сохранения изменчивости, чем акт достижения совершенства.

– Сэнди Мец, Практическое объектно-ориентированное проектирование в Ruby, глава 2

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

А может быть, вы замерзнете. В конце концов, стремление к совершенству может быть сложной задачей. (В более широком, но связанном с этим замечании: Решма Сауджани, основательница Girls Who Code, говорила о том, как склонность быть идеальной может негативно повлиять на то, как девушки подходят к кодированию. Очевидно, это находит отклик у меня).

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

  • Последствия изменения должны быть очевидны;
  • Выгоды от изменения должны быть сопоставимы с затратами на это изменение;
  • Код должен использоваться новыми и неожиданными способами;
  • Качество кода должно быть настолько образцовым, чтобы те, кто его пишет, стремились увековечить эти качества.

Чтобы еще больше закрепить эти ориентиры, давайте поговорим конкретно о классах. Чтобы кодировать в неопределенном настоящем и непредвиденном будущем, давайте сосредоточимся на том, что должно быть сделано.

Класс определяется как объект первого класса и представляет (существительное) часть предметной области, которая должна выполнять наименьшую возможную полезную работу; то есть он должен нести единую ответственность, — считает Мец. Назначение более чем одной ответственности классу затруднило бы его повторное использование. Если он становится настолько раздутым другими обязанностями, он теряет свою универсальность и запрещает использование в других контекстах.

Класс имеет переменные, и каждый экземпляр этого класса создает переменные экземпляра. Переменные экземпляра хранят данные, которые должны быть скрыты от других методов и даже класса, чтобы избежать каких-либо манипуляций. Это можно сделать с помощью `attr_reader`, который позволяет читать, но не записывать доступ к данным; его виртуальное представление будет выглядеть так:

# Implementing attr_reader
class Person
  attr_reader :name
  def initialize(name)
    @name = name
  end
end
# Visual representation of attr_reader
class Person
  def name
    @name
  end
end

Эта изоляция также защищает от будущих изменений. Предположим, что несколько методов ссылаются на переменную экземпляра. Если данные переменной экземпляра изменяются, это потребует изменения только в одном месте, так как изменения в .

По мере развития и роста класса будут появляться методы. Каждый метод должен выполнять одну задачу. Только один. Рефакторинг, даже если окончательный дизайн в данный момент не ясен или если метод требует двухэтапного процесса. Рассмотрим pluralizeFruits, который добавляет одну «s» к каждому элементу фруктов в массиве:

def pluralizeWords(words)
  words.map { |word| word + "s" }
end

Это двухэтапный метод, который очень короткий, но его можно разделить на два метода. Целью этого является выявление скрытых качеств, устранение необходимости в комментариях и поощрение повторного использования или удаления в новом классе. Теперь мы можем использовать метод «итерации», обобщив его назначение.

def pluralizeWords(words)
  words.map { |word| pluralize(word) }
end
def pluralize(word)
  word + "s"
end

Писать код означает не стремиться к совершенству, а довольствоваться «достаточно хорошим». Поначалу это может звучать как противоположность профессиональному программному обеспечению, но Мец отмечает, что некоторые решения невозможно принять без дополнительной информации. И их не следует делать преждевременно. Решения по дизайну могут стать подбрасыванием, и расходы будут начисляться, если позже окажется, что решение было неправильным. Такие проектные решения в условиях неопределенности (например, написание Struct вместо создания нового класса) могут подождать. Будут написаны методы, возникнут зависимости и определена реорганизация.

Спросите себя: какова будущая цена бездействия сегодня?

В эпоху неудач как знака успеха программисты будут с гордостью говорить о взломе кода. Да, кодов ошибок предостаточно, и определенное количество неправильных решений может привести вас к правильному ответу. Но я понял, что написание кода — это практика вдумчивого экспериментирования. Речь идет о прогрессе, а не о совершенстве; речь идет о написании кода с учетом изменчивости и будущего.