Не так давно обсуждали со студентами следующую задачу.
Путь нам надо спроектировать класс, представляющий инвентарь игрока в некоторой компьютерной игре.
Поднимая предмет, игрок может положить его в инвентарь при условии, что в инвентаре достаточно свободного места.
Проверка свободного места в инвентаре - затратная процедура, так как там решается, к примеру,
задача об упаковке.
Есть
принцип разделения команд и запросов (CQRS - Command-Query Responsibility Segregation), согласно которому все методы класса разделяются на два вида - команды и запросы.
Команды модифицируют объекты, а запросы возвращают информацию о них.
При этом запросы не должны обладать абстрактным побочным эффектом, то есть не должны изменять видимое клиенту состояние объектов.
Задача: как правильно спроектировать интерфейс добавления предмета для класса инвентаря?
Существующее решение заключается в следующем - реализован метод, который принимает в качестве аргумента предмет и пытается поместить его в инвентарь.
Если это возможно, то предмет кладётся в инвентарь, при этом метод возвращает true; иначе c инвентарём ничего не происходит, метод возвращает false.
Данное решение нарушает вышеописанный принцип CQRS, так как в одном методе совмещены и команда (помещает предмет, тем самым модифицируя состояние инвентаря), и запрос (возвращает true/false).
Но если разделить этот метод на два, один из которых будет предикатом, возвращающим true, если предмет можно положить, и false в ином случае, а другой метод будет класть предмет в инвентарь, ничего не возвращая, то задача об упаковке будет решаться дважды, что есть не очень хорошо.
Как же быть?