Приобрести навык построения абстракций с применением принципа DIP.
Предположим, что необходимо реализовать следующие пользовательские истории:
Ограничим реализацию этих историй только игровым сервером.
Поступательное движение и поворот - это длительные операции. Под длительностью здесь понимается не то, что временем их выполнения могут быть минуты, часы или годы. Длительность, с точки зрения вычислимости, описанной Дейкстрой (см. занятие №1) означает, что мы можем наблюдать промежуточное состояние этого объекта в процессе выполнения длительной операции. Другими словами, длительная операция - это операция, которая выполняется не атомарным образом. А значит у этой операции есть начало и конец. В рамках этого домашнего задания предстоит реализовать операции начала длительной операции Start и завершения длительной операции End.
Для реализации этой задачи предположим, что у нас все операции, например, move, rotate, имеют один общий интерфейс:
interface ICommand
{
void Execute();
}
Все команды игры хранятся в очереди Queue. Как будет производиться работа с очередью - будет определено в следующих ДЗ. Сейчас достаточно считать, что результат работы команды Start заключается в том, что команда, например, Move или Rotate, ставится в очередь, а результат команды End заключается в том, что соответствующая команда, например, Move или Rotate, удаляется из очереди команд.
Критерии приемки:
В репозитории нет файлов, которые генерируются автоматически в процессе сборки.
Важно! Коэффициент покрытия кода тестами любого PR должен быть больше 90%.
Критерии приемки:
Определен базовый интерфейс ICommand для всех команд, удовлетворящий SOLID.
Для представления скорости, положения объекта в пространстве реализовать класс Vector, который является моделью вектора n-мерного пространства с целочисленными координатами. Для векторов должна быть реализована операция сложения.
Критерии приемки:
Команда MoveCommand выполняет равномерное поступательное движение объекта без деформации за один дискретный момент времени.
Критерии приемки:
Зарегистрировать зависимость "Commands.Move" в IoC, с помощью которой можно разрешить команду MoveCommand по игровому объекту. Для регистрации зависимости определить отдельную команду с префиксом RegisterIoCDependency:
public class RegisterIoCDependencyMoveCommand : ICommand
{
public void Execute()
{
// код, регистрирующий зависимость
}
}
Примечание: Для того, чтобы создать MoveCommand, необходимо создать адаптер IDictionary<string, object> для интерфейса IMovingObject. Задача конструирования Адаптеров по интерфейсу будет рассмотрена в другой лабораторной работе, поэтому в данной задаче используйте разрешение зависимости Ioc.Resolve("Adapters.IMovingObject", obj), где obj - игровой объект. В тестах используйте Mock-объекты, для разрешения этой зависимости.
Критерии приемки:
Для представления угла наклона к оси OX игрового объекта реализовать класс Angle. Внутри Angle представлен как рациональное число, причем знаменатель у всех углов будет одинаковый (его можно сделать статическим полем). Для угла должна быть реализована операция сложения и должна быть возможность вычисления синуса и косинуса от Angle без предварительного явного приведения к типу Double: Math.Cos(angle).
Критерии приемки:
Команда выполняет равномерное поступальное движение объекта без деформации за один дискретный момент времени.
Критерии приемки:
Зарегистрировать зависимость "Commands.Rotate" в IoC, с помощью которой можно разрешить команду MoveCommand по игровому объекту. Для регистрации зависимости определить отдельную команду с префиксом RegisterIoCDependency:
public class RegisterIoCDependencyRotateCommand : ICommand
{
public void Execute()
{
// код, регистрирующий зависимость
}
}
Примечание: Для того, чтобы создать RotateCommand, необходимо создать адаптер IDictionary<string, object> для интерфейса IRotatingObject. Задача конструирования Адаптеров по интерфейсу будет рассмотрена в другой лабораторной работе, поэтому в данной задаче используйте разрешение зависимости Ioc.Resolve("Adapters.IRotatingObject", obj), где obj - игровой объект. В тестах используйте Mock-объекты, для разрешения этой зависимости.
Критерии приемки:
MacroCommand в конструктор получает массив ICommand. Метод Execute последовательно выполняет все команды из массива.
Критерии приемки:
Указание: Для регистрации зависимости определить команду RegisterIoCDependencyMacroCommand:
public class RegisterIoCDependencyMacroCommand : ICommand
{
public void Execute()
{
// код, регистрирующий зависимость
}
}
Критерии приемки:
Предположим, что при разрешении зависимости вида "Specs.<операция>" можно получить список наименований команд, которые образуют макрокоманду. Используя данный список и IoC необходимо получить Команды, сформировать из них список, по которому создать экземпляр MacroCommand.
class CreateMacroCommandStrategy(string commandSpec)
{
ICommand Resolve(object[] args)
{
// код по конструированию зависимости
}
}
Критерии приемки:
Примечание: Считать, что зависимости "Specs.Move" и "Specs.Rotate" - заданы (это другая лабораторная работа). Для тестов задать зависимости с помощью Mock-объектов.
Указание: Для регистрации зависимости определить команду RegisterIoCDependencyMacroMoveRotate:
public class RegisterIoCDependencyMacroMoveRotate : ICommand
{
public void Execute()
{
// код, регистрирующий зависимость
}
}
SendCommand в конструктор получает команду ICommand и интерфейс ICommandReceiver. В методе Execute SendCommand вызывается метод Receive интерфейса ICommandReceiver, в который передается длительная команда.
Критерии приемки:
Указание: Для регистрации зависимости определить команду RegisterIoCDependencySendCommand:
public class RegisterIoCDependencySendCommand : ICommand
{
public void Execute()
{
// код, регистрирующий зависимость
}
}
Критерии приемки:
public interface ICommandInjectable
{
void Inject(ICommand command);
}
Команда реализует два интерфейса ICommand и ICommandIjectable.
Критерии приемки:
Указание: Для регистрации зависимости определить команду RegisterMacroCommand:
public class RegisterDependencyCommandInjectableCommand : ICommand
{
public void Execute()
{
// код, регистрирующий зависимость
}
}
Критерии приемки:
Ioc.Resolve<ICommand>("Commands.CommadInjectable");
Ioc.Resolve<ICommandInjectable>("Commands.CommadInjectable");
Ioc.Resolve<CommandInjectableCommand>("Commands.CommadInjectable");
Необходимо так определить зависимость, чтобы следующий код позволял получать команду:
IDictionary<string, object> order = ...; // приказ о старте длительной операции
Ioc.Resolve<ICommand>("Actions.Start", order);
Сама регистрация зависимости должна быть выполнена в команде RegisterIoCDepenedncyActionsStart
public class RegisterIoCDependencyActionsStart : ICommand
{
public void Execute()
{
// здесь код для регистрации зависимостей
}
}
Примечание: Все дополнительные зависимости, которые будут использованы при реализации, кроме зависимостей, которые были оговорены выше, должны быть также зарегистрированы.
Критерии приемки:
Необходимо так определить зависимость, чтобы следующий код позволял получать команду:
IDictionary<string, object> order = ...; // приказ об остановке длительной операции
Ioc.Resolve<ICommand>("Actions.Stop", order);
Сама регистрация зависимости должна быть выполнена в команде RegisterIoCDepenedncyActionsStop
public class RegisterIoCDependencyActionsStop : ICommand
{
public void Execute()
{
// здесь код для регистрации зависимостей
}
}
Критерии приемки: