Skip to content

Latest commit

 

History

History
152 lines (118 loc) · 8.74 KB

File metadata and controls

152 lines (118 loc) · 8.74 KB

Размер имеет значение

В детстве мы играли в простую, но удивительно поучительную игру. Дети становились в круг, и один из них начинал перекидывать мяч. Но это был не просто мяч — это была «горячая картошка». И правила были предельно ясны: поймал — тут же бросай дальше. Максимум одна секунда. Кто задержал — проиграл. Никаких пауз, планов и стратегий. Только действие. Только передача.

Если ты хоть на мгновение задумался — обжёгся. Здесь нет времени для сомнений: нужно полагаться на интуицию, играть легко и не мешать ходу игры.

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

У каждого должна быть одна цель — передать задачу и не тормозить процесс. Ассоциируйте это как:

  • Класс — это игрок.
  • Метод — это пас. Он может быть левой рукой, правой, можно схватить или отбросить мяч.
  • Строки — это момент перед броском.

Когда момент перед пасом выглядит вот так:

// Слишком длинный метод [✗]
public function export(string $key)
{
    // ...
    // ...
    // 1000 строк кода
    
    return $result;
}

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

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

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

Худшие разработчики гордятся таким кодом: «Он сложный», «Он умный», «Он крутой». А если его трудно читать, советуют лучше разобраться в основе. Но на самом деле это просто неумение передать мяч как можно быстрее. Некоторые разработчики пытаются исправить проблему, формально дробя код на отдельные методы:

// Слишком длинный метод [✗]
public function export(string $key)
{
    // Загрузка данных
    // Валидация
    // Преобразование
    // Генерация отчёта
    // Сохранение в файл
    // Отправка по почте
    // И ещё десяток шагов...
    $this->step();
    $this->step();
    $this->step();
    
    $this->load();
    $this->validate();
    $this->transform();
    $this->generateReport();
    // И ещё десяток шагов...
    $this->sendMail();
    
    return $result;
}

На первый взгляд — красиво, ведь метод export() записан условно в пять строчек. Но где тут само «сердце»? Вам приходится прыгать из метода в метод, искать смысл: «а, здесь что-то подгружается, а вот здесь валидируется, а вот здесь ещё что-то происходит…». Глаз бегает по коду без чувства завершённости.

Правильно дробить — значит давать каждому этапу собственную осмысленную ответственность, а не делать «пустую оболочку» ради экономии строк. Если метод публичный, он должен отражать высокоуровневый шаг, понятный «с первого взгляда». А приватные методы должны решать действительно отдельный логический блок, а не просто «задёргивать» следующий вызов без собственной логики.

Хороший публичный метод должен вызывать у вас реакцию: «Да, это целостный шаг!» Например:

// Хорошо [✓]
$document = Document::find(1);

$content = $document->export(Excel::class);

$user->notify(ExportNotification::class, [
    'content' => $content->toString(),
]);

Каждая строка — как законченный абзац. Здесь нет прыжков по стеку. Всё перед глазами и мы наглядно видим, что сделали пас.

Но даже когда мы избавились от процедурного стиля «шаг1», «шаг2», «шаг3», очень легко попасть в ловушку: кто должен принимать решения и в какой форме объекты должны взаимодействовать друг с другом?

Например:

// Плохо [✗]
$document = Document::find(1);

if($document->isPublished()) {
    $content = $document->export(Excel::class);
}

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

Объект самостоятельно определяет, с кем и как ему взаимодействовать — внешний код лишь описывает намерение. Такой подход повышает модульность и гибкость архитектуры.

// Плохо [✗]
if ($user->isAdmin() || $user->hasRole('manager')) {
    $content = $document->export(Excel::class);
}
// Хорошо [✓]
if ($user->canExport($document)) {
    $content = $document->export(Excel::class);
}

Это один из важных принципов, который помогает сделать код объектов лаконичным, звучит так:

Не спрашивай объект о его данных, чтобы принять решение — скажи объекту, что делать.

Игра «горячая картошка» научила нас — не задерживать ответственность. А принцип объектно-ориентированного проектирования — говорить, а не спрашивать; не задавать лишних вопросов, а формулировать намерения. В следующих разделах разберём конкретные техники, которые позволяют быстрее передать «мяч» дальше.