Command-query Separation (CQS) — принцип программирования, изобретённый Бертандом Майером, создателем языка 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
или вычисление максимального числа в массиве, может выполнить какое-то деструктивное действие. С другой стороны, на практике такой код иногда попадается и теперь вы знаете, как правильно его исправить.
Вам ответят команда поддержки Хекслета или другие студенты.
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт