Подтвердить что ты не робот

Управление жизненным циклом объекта и контейнеры IoC

Я обновляю игру от одиночного игрока к мультиплееру. В этом случае игра была первоначально написана, когда большинство классов были одиночными. например существовал единственный объект Player, один объект GameState и т.д. То есть каждый из этих объектов жил до тех пор, пока приложение.

Теперь, когда несколько игроков могут играть одновременно, я, очевидно, должен поддерживать создание более чем одного объекта Player, объекта GameState и т.д. В ходе работы над этим я понял, что у большинства объектов есть одна из трех жизней

  • Продолжительность жизни приложения, например. проводник для навигации
  • Продолжительность жизни проигрывателя, например. параметр SettingsModel для текущего проигрывателя
  • Продолжительность жизни игры, например. GameState для текущей игры

Мне любопытно, как другие относятся к созданию этих разных объектов с использованием контейнера IoC. Я хочу избежать создания классов factory для каждого класса с игровой или игровой продолжительностью жизни.

4b9b3361

Ответ 1

Здесь приведен пример IOC, который может помочь. Проект называется IOC-with-Ninject. Он использует Ninject плюс класс контейнера IOC для управления всеми объектами жизненного цикла объекта. Вам нужно будет немного сделать исследование Ninject, чтобы настроить его на конкретные нужды, но это ваше контейнерное решение IOC (IMHO), если вы используя .NET и поможет вам организовать свою базу кода. Это личный выбор, но я клянусь этим. Если вы не используете .NET, это все равно даст вам простой образец. Приветствия.

Ответ 2

Многие контейнеры IoC имеют пользовательские диапазоны жизненного цикла, которыми вы можете управлять по своему усмотрению. Например, в Ninject вы можете определить свой собственный круг жизненного цикла следующим образом:

kernel.Bind<IService>().To<Service>().InScope((c, o) => yourCustomeScope);

Пока переменная yourCustomeScope не изменилась, один экземпляр объекта Service возвращается каждый раз, когда ядро ​​получает запрос на IService. Как только переменная yourCustomeScope изменится, новый экземпляр службы будет создан при следующем запросе для IService. yourCustomeScope может быть текущим экземпляром игрока, игровым объектом или чем-либо, что вы хотите изменить для жизни объекта службы, на основе его изменения ссылки.

Однако объекты, о которых вы только что упоминали, скорее всего являются объектами, а не службами, для которых я не думаю, что инъекция - хорошая идея.

Ответ 3

По моему опыту подход фабрик работает лучше всего.

Контроль продолжительности жизни экземпляра неудобен для поддержки и требует усилий, знания всех требований и зависимостей жизни классов, времени для настройки и управления конфигурацией. В то же время использование фабрик является естественным и специфичным для кода.

Создание фабрик (реализация) можно было бы избежать, используя прокси-заводы. У вас также могут быть фабрики, возвращающие общие аргументы для дальнейшего снижения потребностей создания фабрик (интерфейсов).

Если требуется слишком много заводов, я предлагаю просмотреть поток кода.

Ответ 4

Я думаю, что это отчасти является перефразированием некоторых комментариев предыдущих ответов, но я попытался продемонстрировать немного расширить некоторые рассуждения.

Как только вы попадаете в область управления временем жизни вложенных объектов, вы, вероятно, должны создавать заводы для этих объектов.

Основная проблема заключается в том, что корневой состав не знает, каким будет экологический контекст вызова, который должен создать объект.

Думаю, я должен сделать шаг назад и объяснить в этот момент.

Полученная мудрость при инъекции зависимостей состоит в том, чтобы иметь корень композиции, где-то рядом с точкой входа кода. Есть много веских причин для этого, которые нетрудно найти в Интернете, поэтому я не буду вдаваться в это здесь.

Корень композиции - это то, где вы сопоставляете свои интерфейсы (обычно, но, возможно, объекты) с их внедрением. Вы можете передать информацию, доступную в этот момент для конструктора. Таким образом, вы можете передать ссылку на объект, время жизни которого является текущим во время выполнения корня композиции.

Однако, если время жизни корня композиции не перекрывается с временем жизни объекта, который вы хотите создать, вам нужно отложить выполнение конструктора до тех пор, пока объект не будет создан. Вот почему вам нужно иметь factory. Вы можете передать метод factory для вашего сопоставления в этот момент и, таким образом, передать информацию, необходимую для создания объекта, но разрешить создание в то время, когда это необходимо, когда не выполняется корень композиции.

Вам не нужен класс factory для выполнения этого метода factory, но метод factory может быть встроен, и поэтому накладные расходы кода не намного больше, чем если бы мы создавали объекты в композиции маршрут.

Если у нас есть проект с двумя службами, где первая служба зависит от первой, и мы хотим, чтобы время жизни второй службы начиналось, когда мы создаем первую услугу, у нас может быть что-то вроде следующего. (Я использую ninject, чтобы привести пример кода, но я ожидаю, что другие контейнеры IOC будут работать аналогично в этом отношении.)

`   
    public class Service1:IService
    {
        private Func<IService>serviceFactoryMethod _Service2Factory;
        public Service1(Func<IService>service2FactoryMethod)
        {
            _Service2Factory=service2FactoryMethod;
        }

        public void DoSomethingUsingService2()
        {
            var service2=_Service2Factory();
            service2.DoSomething();
        }
    }

    public class MainClass
    {
        public void CompositionRoot()
        {
            var kernel= new StandardKernel();
            kernel.Bind.ToMethod(m=>
            { 
               return new Service1(m.Kernel.Get<IService2>());
            }
        }
    }

`   

В этом примере не рассматриваются способы управления жизненным циклом приложений, игроков и игр, но, надеюсь, он дает достаточные сведения о том, как удалить жизненные проблемы, связанные с инъекцией зависимостей.

Боковое замечание: при использовании Ninject вы сможете изменить область действия Service2, чтобы управлять своей жизнью, чтобы перейти на всю жизнь Service1. Например, если бы вы знали, что каждый экземпляр игры должен был произойти в своем потоке (это может быть несколько маловероятно), вы можете использовать InThreadScope для игры.