HTML Diff
1 added 1 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В этом уроке мы поговорим о<strong>CQS</strong>(<em>Command-query Separation</em>). Это принцип программирования, изобретенный Бертандом Майером, создателем языка Eiffel.</p>
1 <p>В этом уроке мы поговорим о<strong>CQS</strong>(<em>Command-query Separation</em>). Это принцип программирования, изобретенный Бертандом Майером, создателем языка Eiffel.</p>
2 <p>Этот принцип утверждает, что каждая функция считается:</p>
2 <p>Этот принцип утверждает, что каждая функция считается:</p>
3 <ul><li>Либо командой, которая выполняет действие (<em>action</em>)</li>
3 <ul><li>Либо командой, которая выполняет действие (<em>action</em>)</li>
4 <li>Либо запросом, который извлекает данные (<em>query</em>)</li>
4 <li>Либо запросом, который извлекает данные (<em>query</em>)</li>
5 </ul><p>При этом функция не может быть командой и запросом одновременно. Команда всегда связана с выполнением побочных эффектов, а чистые функции возможны только для запросов.</p>
5 </ul><p>При этом функция не может быть командой и запросом одновременно. Команда всегда связана с выполнением побочных эффектов, а чистые функции возможны только для запросов.</p>
6 <h2>Как работает команда</h2>
6 <h2>Как работает команда</h2>
7 <p>Рассмотрим такой пример:</p>
7 <p>Рассмотрим такой пример:</p>
8 <p>Согласно принципу CQS, функция save считается командой. Ее единственная задача - это возвращать успешность своего выполнения как значение true или false. По той же логике может быть null, как в случае с print_r. Если мы попробуем вернуть какие-то осмысленные данные с помощью этой функции, это будет считаться нарушением CQS. Но в некоторых ситуациях этот принцип невозможно соблюсти. Например, открытие файла на запись возвращает файловый дескриптор - идентификатор, через который происходят манипуляции с файлом.</p>
8 <p>Согласно принципу CQS, функция save считается командой. Ее единственная задача - это возвращать успешность своего выполнения как значение true или false. По той же логике может быть null, как в случае с print_r. Если мы попробуем вернуть какие-то осмысленные данные с помощью этой функции, это будет считаться нарушением CQS. Но в некоторых ситуациях этот принцип невозможно соблюсти. Например, открытие файла на запись возвращает файловый дескриптор - идентификатор, через который происходят манипуляции с файлом.</p>
9 <p>Отделение команд от запросов тесно связано с идеями, описанными в уроке о чистых функциях.</p>
9 <p>Отделение команд от запросов тесно связано с идеями, описанными в уроке о чистых функциях.</p>
10 <p>Команды по определению выполняют<strong>недетерминированный</strong>код с побочными эффектами, потому что повторный вызов команды приводит либо к ошибке, либо к повторному выполнению действия. Вообще команды можно сделать детерминированными, но это не лучшее решение - как правило, такой код скрывает логические ошибки.</p>
10 <p>Команды по определению выполняют<strong>недетерминированный</strong>код с побочными эффектами, потому что повторный вызов команды приводит либо к ошибке, либо к повторному выполнению действия. Вообще команды можно сделать детерминированными, но это не лучшее решение - как правило, такой код скрывает логические ошибки.</p>
11 - <p>Следовательно, чтобы отделить чистый код от кода с побочными эффектами, мы можем выделить запрос (возврат данных) из команды в отдельную функцию. Как мы увидим позже, запросы можно выполнять множество раз, не боясь что-то сломать:</p>
11 + <p>Следовательно, чтобы отделить чистый код от кода с побочными эффектами, мы можем выделить запрос (возврат данных) из коанды в отдельную функцию. Как мы увидим позже, запросы можно выполнять множество раз, не боясь что-то сломать:</p>
12 <h2>Как работает запрос</h2>
12 <h2>Как работает запрос</h2>
13 <p>Рассмотрим такой фрагмент кода:</p>
13 <p>Рассмотрим такой фрагмент кода:</p>
14 <p>Функцию isAdmin можно воспринимать как:</p>
14 <p>Функцию isAdmin можно воспринимать как:</p>
15 <ul><li>Предикат</li>
15 <ul><li>Предикат</li>
16 <li>Типичный запрос (<em>query</em>)</li>
16 <li>Типичный запрос (<em>query</em>)</li>
17 <li>Вопрос "Является ли пользователь администратором?"</li>
17 <li>Вопрос "Является ли пользователь администратором?"</li>
18 </ul><p>С точки зрения CQS такая функция не может изменить состояние системы - например, поменять дату проверки на администратора внутри пользователя или сделать пользователя администратором.</p>
18 </ul><p>С точки зрения CQS такая функция не может изменить состояние системы - например, поменять дату проверки на администратора внутри пользователя или сделать пользователя администратором.</p>
19 <p>Это противоречит не только CQS, но и здравому смыслу. В отличие от предыдущего примера, в случае предикатов true и false показывают не успешность выполнения функции, а ответ на этот запрос.</p>
19 <p>Это противоречит не только CQS, но и здравому смыслу. В отличие от предыдущего примера, в случае предикатов true и false показывают не успешность выполнения функции, а ответ на этот запрос.</p>
20 <p>Взгляните на пример работы функции, которая меняет исходные данные:</p>
20 <p>Взгляните на пример работы функции, которая меняет исходные данные:</p>
21 <p>Если сделать еще один вызов takeKids($users), то выполнение кода, скорее всего, завершится с ошибкой, потому что изменилась структура исходного массива. Такое поведение функции-запроса противоестественно. CQS имеет альтернативную формулировку, которая отлично характеризует код выше: "Задавая вопрос, не изменяй ответ".</p>
21 <p>Если сделать еще один вызов takeKids($users), то выполнение кода, скорее всего, завершится с ошибкой, потому что изменилась структура исходного массива. Такое поведение функции-запроса противоестественно. CQS имеет альтернативную формулировку, которая отлично характеризует код выше: "Задавая вопрос, не изменяй ответ".</p>
22 <p>К запросам относятся и любые вычисления:</p>
22 <p>К запросам относятся и любые вычисления:</p>
23 <p>Этот код не создает никаких побочных эффектов и детерминирован. Его можно вызывать сколько угодно раз без риска получить ошибку или неверный результат.</p>
23 <p>Этот код не создает никаких побочных эффектов и детерминирован. Его можно вызывать сколько угодно раз без риска получить ошибку или неверный результат.</p>
24 <p>Отсутствие изменений в запросах - это очень важный принцип, который нужно соблюдать всегда. Даже на интуитивном уровне ни один человек не ожидает, что проверка isAdmin или вычисление максимального числа в массиве может выполнить какое-то деструктивное действие. С другой стороны, на практике такой код иногда попадается. Теперь вы знаете, как его исправить.</p>
24 <p>Отсутствие изменений в запросах - это очень важный принцип, который нужно соблюдать всегда. Даже на интуитивном уровне ни один человек не ожидает, что проверка isAdmin или вычисление максимального числа в массиве может выполнить какое-то деструктивное действие. С другой стороны, на практике такой код иногда попадается. Теперь вы знаете, как его исправить.</p>