Этот проект демонстрирует реализацию паттерна Команда (Command) для системы Undo/Redo в консольной RPG игре.
Паттерн Command инкапсулирует запрос как объект, позволяя:
- Отменять выполненные действия (Undo)
- Хранить историю действий
- Откладывать выполнение команд
- Поддерживать очередь команд
┌─────────────────────────────────────────────────────────────────────────────┐
│ ПАТТЕРН КОМАНДА (COMMAND) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────────┐ │
│ │ Client │ │ Invoker │ │ Command │ │
│ │ (Program) │─────▶│ (InputHandler) │─────▶│ (ICommand) │ │
│ │ │ │ │ │ │ │
│ │ - создаёт │ │ - хранит историю │ │ + Execute() │ │
│ │ команды │ │ - вызывает │ │ + Undo() │ │
│ │ │ │ Execute() │ │ △ │ │
│ └─────────────┘ │ - вызывает │ │ │ │ │
│ │ Undo() │ └─────────┼───────────┘ │
│ └────────┬─────────┘ │ │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Concrete Commands │ │
│ ├──────────────────┬──────────────┬───────────────┤ │
│ │ MoveCommand │ AttackCommand│ HealCommand │ │
│ ├──────────────────┼──────────────┼───────────────┤ │
│ │ - _player │ - _enemy │ - _player │ │
│ │ - _dx, _dy │ - _damage │ - _healPower │ │
│ │ - _previousX/Y │ - _prevHealth│ - _prevHealth │ │
│ └────────┬─────────┴──────┬───────┴───────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Receivers │ │
│ ├──────────────────┬──────────────────────────────┤ │
│ │ Player │ Enemy │ │
│ ├──────────────────┼──────────────────────────────┤ │
│ │ + X, Y │ + Health │ │
│ │ + Health │ + TakeDamage() │ │
│ │ + Move() │ │ │
│ │ + Heal() │ │ │
│ └──────────────────┴──────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ПОТОК ВЫПОЛНЕНИЯ │ │
│ │ │ │
│ │ 1. Client создаёт Command ──▶ 2. Invoker.ExecuteCommand() │ │
│ │ │ │
│ │ 3. Command сохраняет состояние ──▶ 4. Command вызывает Receiver │ │
│ │ │ │
│ │ 5. Invoker сохраняет Command в Stack ──▶ 6. Undo() вызывает │ │
│ │ Pop() и восстанавливает │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Command/ # Корневая папка проекта
│
├── Commands/ # Реализации команд
│ ├── ICommand.cs # Интерфейс команды
│ ├── MoveCommand.cs # Команда перемещения
│ ├── AttackCommand.cs # Команда атаки
│ └── HealCommand.cs # Команда лечения (бонус)
│
├── Helpers/ # Вспомогательные классы
│ └── DirectionHelper.cs # Определение направлений движения
│
├── Player.cs # Игрок (получатель команд)
├── Enemy.cs # Враг (получатель команд)
├── InputHandler.cs # Отправитель команд + история (Invoker)
├── Program.cs # Клиентский код
│
└── README.md # Документация
| Команда | Действие | Отмена |
|---|---|---|
MoveCommand |
Перемещение игрока по карте (8 направлений) | Возврат на предыдущую позицию |
AttackCommand |
Нанесение урона врагу | Восстановление здоровья врага |
HealCommand |
Лечение игрока | Возврат к предыдущему здоровью |
Поддерживаемые направления движения:
- Основные: север, юг, запад, восток
- Диагональные: северо-восток, северо-запад, юго-восток, юго-запад
// Создание игровых объектов
Player player = new Player(5, 5, 50);
Enemy goblin = new Enemy(100);
InputHandler input = new InputHandler();
// Выполнение команд
input.ExecuteCommand(new MoveCommand(player, 1, 0)); // движение на восток
input.ExecuteCommand(new AttackCommand(goblin, 15)); // атака врага
input.ExecuteCommand(new HealCommand(player, 25)); // лечение игрока
// Отмена действий (LIFO)
input.UndoLastCommand(); // отмена лечения
input.UndoLastCommand(); // отмена атаки
input.UndoLastCommand(); // отмена движения- ✅ Защита от null — команды проверяют получателей на null
- ✅ Валидация данных — отрицательный урон/лечение преобразуется в 0
- ✅ Защита от повторной отмены — проверка состояния перед отменой
- ✅ Сохранение состояния — каждая команда хранит предыдущее состояние
- ✅ Обработка граничных случаев — здоровье не уходит ниже 1
- ✅ Поддержка 8 направлений — полная свобода перемещения
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌────────────────────┐ │
│ │ «interface» │ │
│ │ ICommand │ │
│ ├────────────────────┤ │
│ │ + Execute() │ │
│ │ + Undo() │ │
│ └────────┬───────────┘ │
│ │ │
│ △ │
│ ┌────┴────┬───────────────────┐ │
│ │ │ │ │
│ ┌───┴────┐ ┌───┴────┐ ┌───┴────┐ │
│ │ Move │ │Attack │ │ Heal │ │
│ │Command │ │Command │ │Command │ │
│ └───┬────┘ └───┬────┘ └───┬────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │Player │ │Enemy │ │Player │ │
│ └───────┘ └───────┘ └───────┘ │
│ │ △ │
│ │ (только для MoveCommand) │ │
│ ▼ ▼ │ │
│ ┌────────────────────────────┐ │ │
│ │ DirectionHelper │ │ │
│ │ (static) │ ──┘ │
│ │ + GetDirectionName() │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────┐ │
│ │ InputHandler │ │
│ ├────────────────────┤ │
│ │ - _commandHistory │ │
│ │ (Stack) │ │
│ ├────────────────────┤ │
│ │ + ExecuteCommand() │ │
│ │ + UndoLastCommand()│ │
│ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
- Инкапсуляцию запроса как объекта — каждая команда самостоятельна
- Отделение отправителя от получателя — InputHandler не знает о Player и Enemy
- Возможность отмены операций — полная поддержка Undo
- Расширяемость — добавление новых команд не требует изменения существующего кода
Vladimir Vaize | GitHub | Telegram Channel