Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Разделение команд и запросов PHP: Функции

В этом уроке мы поговорим о CQS (Command-query Separation). Это принцип программирования, изобретенный Бертандом Майером, создателем языка Eiffel.

Этот принцип утверждает, что каждая функция считается:

  • Либо командой, которая выполняет действие (action)
  • Либо запросом, который извлекает данные (query)

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

Как работает команда

Рассмотрим такой пример:

<?php

// Возвращает true или false как результат своего выполнения
save($user);

Согласно принципу CQS, функция save считается командой. Ее единственная задача — это возвращать успешность своего выполнения как значение true или false. По той же логике может быть null, как в случае с print_r. Если мы попробуем вернуть какие-то осмысленные данные с помощью этой функции, это будет считаться нарушением CQS. Но в некоторых ситуациях этот принцип невозможно соблюсти. Например, открытие файла на запись возвращает файловый дескриптор — идентификатор, через который происходят манипуляции с файлом.

Отделение команд от запросов тесно связано с идеями, описанными в уроке о чистых функциях.

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

Следовательно, чтобы отделить чистый код от кода с побочными эффектами, мы можем выделить запрос (возврат данных) из команды в отдельную функцию. Как мы увидим позже, запросы можно выполнять множество раз, не боясь что-то сломать:

<?php

$file = fopen('/etc/hosts', 'r');

Как работает запрос

Рассмотрим такой фрагмент кода:

<?php

// Возвращает true или false
isAdmin($user);

Функцию isAdmin можно воспринимать как:

  • Предикат
  • Типичный запрос (query)
  • Вопрос «Является ли пользователь администратором?»

С точки зрения CQS такая функция не может изменить состояние системы — например, поменять дату проверки на администратора внутри пользователя или сделать пользователя администратором.

Это противоречит не только CQS, но и здравому смыслу. В отличие от предыдущего примера, в случае предикатов true и false показывают не успешность выполнения функции, а ответ на этот запрос.

Взгляните на пример работы функции, которая меняет исходные данные:

<?php

$users = [
    ['name' => 'Stan', 'kids' => ['John', 'Mary']],
    ['name' => 'Donald', 'kids' => ['James']],
    ['name' => 'Lily', 'kids' => []],
    ['name' => 'Julian', 'kids' => []]
];

// Функция takeKids() возвращает массив детей всех пользователей
takeKids($users); // ['John', 'Mary', 'James']
// На самом деле внутри она меняет массив $users и возвращает его наружу
print_r($users); // => ['John', 'Mary', 'James']

Если сделать еще один вызов takeKids($users), то выполнение кода, скорее всего, завершится с ошибкой, потому что изменилась структура исходного массива. Такое поведение функции-запроса противоестественно. CQS имеет альтернативную формулировку, которая отлично характеризует код выше: «Задавая вопрос, не изменяй ответ».

К запросам относятся и любые вычисления:

<?php

$max = max([1, 30, 4]);

Этот код не создает никаких побочных эффектов и детерминирован. Его можно вызывать сколько угодно раз без риска получить ошибку или неверный результат.

Отсутствие изменений в запросах — это очень важный принцип, который нужно соблюдать всегда. Даже на интуитивном уровне ни один человек не ожидает, что проверка isAdmin или вычисление максимального числа в массиве может выполнить какое-то деструктивное действие. С другой стороны, на практике такой код иногда попадается. Теперь вы знаете, как его исправить.


Дополнительные материалы

  1. Command-query Separation
  2. Принцип наименьшего удивления

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 25 апреля

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»