Абстрактный
За последние несколько месяцев я программировал легкий игровой движок на базе С# с абстракцией API и системой сущностей/компонентов/скриптов. Вся идея заключается в том, чтобы облегчить процесс разработки игры в XNA, SlimDX и т.д., Создав архитектуру, подобную архитектуре Unity.
Проблемы проектирования
Как известно большинству разработчиков игр, существует множество разных сервисов, необходимых для доступа к вашему коду. Многие разработчики прибегают к использованию глобальных статических экземпляров, например. менеджер Render (или композитор), Scene, Graphicsdevice (DX), Logger, Input, Viewport, Window и т.д. Существуют альтернативные подходы к глобальным статическим экземплярам/синглонам. Один из них - дать каждому классу экземпляр классов, к которым он должен получить доступ, либо через инсталляцию зависимостей конструктора, либо конструктор/свойство (DI), другой - использовать глобальный локатор службы, например StructureMap ObjectFactory, где локатор службы обычно настраивается как контейнер IoC.
Инъекция зависимостей
Я выбрал путь DI по многим причинам. Наиболее очевидным из которых является тестируемость, программирование против интерфейсов и наличие всех зависимостей каждого класса, предоставляемого им через конструктор, эти классы легко тестируются, поскольку тестовый контейнер может создавать требуемые сервисы или издеваться над ними и загружать их в каждый испытуемый класс. Другая причина для выполнения DI/IoC была, верьте или нет, для повышения удобочитаемости кода. Нет более огромного процесса инициализации создания экземпляров всех различных сервисов и ручного создания классов со ссылками на требуемые сервисы. Конфигурирование ядра (NInject)/Registry (StructureMap) удобно дает единую точку конфигурации для движка/игры, в которой собираются и настраиваются сервисные реализации.
Мои проблемы
- Мне часто кажется, что я создаю интерфейсы для интерфейсов.
- Моя производительность резко снизилась, так как все, что я делаю, это беспокоиться о том, как делать вещи DI-way, а не быстро и просто глобально статически.
- В некоторых случаях, например, при создании экземпляров новых сущностей во время выполнения требуется создать доступ к контейнеру/ядру IoC для создания экземпляра. Это создает зависимость от самого контейнера IoC (ObjectFactory в SM, экземпляр ядра в Ninject), что действительно противоречит причине его использования в первую очередь. Как это можно решить? Приводятся абстрактные заводы, но это еще больше усложняет код.
- В зависимости от требований к сервису конструкторы некоторых классов могут стать очень большими, что сделает класс совершенно бесполезным в других контекстах, где и если IoC не используется.
В основном выполнение DI/IoC резко замедляет мою производительность, а в некоторых случаях еще больше усложняет код и архитектуру. Поэтому я не уверен, что это путь, за которым я должен следовать, или просто отказаться и сделать то, что старомодно. Я не ищу ни одного ответа, говоря, что я должен или не должен делать, кроме обсуждения вопроса о том, стоит ли использовать DI в долгосрочной перспективе, а не использовать глобальный статический/однопользовательский способ, возможные плюсы и минусы, которые я забыл и возможные решения моих проблем, перечисленных выше, при работе с DI.