CQS, Command-Query Separation
https://giga.chat/link/gcswPZKUTD
Английское наименование: Command-Query Separation (CQS).
Русская трактовка: Это принцип объектно-ориентированного программирования, который гласит: метод должен быть либо командой, которая выполняет действие (и изменяет состояние системы), либо запросом, который возвращает данные, но не тем и другим одновременно.
Этот принцип был предложен Бертраном Мейером, создателем языка Eiffel. Он помогает сделать код более предсказуемым, простым для понимания и менее подверженным ошибкам.
Детальное объяснение
Принцип разделяет все методы в системе на две непересекающиеся категории.
Команды (Commands)
- Назначение: изменяют состояние объекта или системы. Они выполняют какое-либо действие.
- Возвращаемое значение: не возвращают данные (кроме, возможно, кода операции или статуса ошибки). В строгой трактовке CQS они возвращают
void. - Пример: метод
SaveToDatabase(),DeleteUser(int id),SetName(string name). Вызов такого метода имеет побочные эффекты: он что-то меняет в базе данных, в памяти или во внешнем мире.
Запросы (Queries)
- Назначение: возвращают данные о текущем состоянии объекта или системы. Они не должны ничего менять.
- Возвращаемое значение: всегда возвращают результат (данные).
- Пример: метод
GetUserById(int id),GetAllOrders(),IsUserLoggedIn(). Вызов одного и того же запроса несколько раз подряд с одинаковыми аргументами не должен приводить к разным результатам или каким-либо побочным эффектам.
Почему это важно?
- Предсказуемость и читаемость. Глядя на вызов метода, разработчик сразу понимает, что произойдёт. Если это запрос (
Get...,Is...,Has...), он знает, что состояние системы не изменится. Если это команда (Save...,Update...,Delete...), он ожидает побочных эффектов. - Безопасность и кэширование. Запросы можно безопасно вызывать многократно, их результаты можно кэшировать для повышения производительности. С командами так поступать нельзя, так как повторный вызов может привести к дублированию операций (например, двойному списанию денег).
- Упрощение тестирования. Методы, которые не имеют побочных эффектов (чистые функции/запросы), гораздо проще тестировать. Их результат зависит только от входных данных, а не от скрытого состояния системы.
Пример нарушения принципа (антипаттерн)
Рассмотрим метод, который нарушает CQS:
// Нарушает CQS: и возвращает данные, и изменяет состояние
public int Pop()
{
// Изменяет состояние стека, удаляя верхний элемент
return _stack.Pop();
}В этом примере метод Pop является командой (он изменяет стек), но при этом он возвращает значение. Это может привести к ошибкам, если разработчик вызовет метод ради побочного эффекта, не ожидая возвращаемого значения, или наоборот.
Строгое следование CQS разделило бы эту операцию на два метода:
T Peek()— запрос, который возвращает верхний элемент, не изменяя стек.void Pop()— команда, которая просто удаляет верхний элемент.
Практическое применение в вашей архитектуре
В контексте вашей системы на основе поведений (Behaviors) этот принцип можно применить к обработке событий:
- Запрос: поведение может запрашивать данные из состояния ячейки (
FractalCellState), чтобы принять решение о дальнейшей обработке. Например, методCanHandleAsync(event)по своей сути является запросом — он проверяет состояние и возвращаетtrueилиfalse, ничего не меняя. - Команда: метод
HandleAsync(event)является командой. Он получает событие и на его основе изменяет состояние ячейки или публикует новые события в шине.
Такое разделение делает логику каждого поведения более чистой и предсказуемой. Вы точно знаете, что методы проверки (CanHandle) безопасны для вызова в любом месте, а методы обработки (Handle) несут в себе логику изменения.
Комментариев нет:
Отправить комментарий