2012-11-17 14 views
12

Aktualizuję grę z trybu dla jednego gracza do trybu wieloosobowego. W tym przypadku gra została oryginalnie napisana, a większość klas jest pojedynczym instancją. na przykład był pojedynczy obiekt Gracza, pojedynczy obiekt GameState itd. Oznacza to, że każdy z tych obiektów był tak długi, jak aplikacja.Zarządzanie cyklem życia obiektów i kontenerami IoC

Teraz, gdy więcej niż jeden gracz może grać na raz, oczywiście potrzebuję wspierać tworzenie więcej niż jednego obiektu Gracza, obiektu GameState, itp. W trakcie pracy nad tym zdałem sobie sprawę, że większość obiektów ma jedną z trzech lifespans:

  1. Żywotność aplikacji, np. Dyrygent do obsługi nawigacji
  2. Żywotność odtwarzacza, np. SettingsViewModel dla bieżącego gracza
  3. Żywotność gry, np. GameState dla bieżącej gry

Jestem ciekawy, jak inni radzą sobie z tworzeniem tych różnych obiektów za pomocą kontenera IoC. Chcę uniknąć tworzenia klas fabrycznych dla każdej klasy z czasem życia gracza lub gry.

+0

Konfiguracja stylu życia/życia jest zwykle czymś, co obsługuje większość kontenerów DI. O jakiej platformie i ramach DI mówisz? – Steven

+0

Mam świadomość, że niektóre kontenery obsługują dożywotnio zarządzanie w pewnym zakresie, ale wyobraź sobie klasę, która jako parametr przyjmuje parametr IPlayer (tak jak wiele klas robi w tym przypadku). Jeśli istnieje jeden kontener, to w jaki sposób zarejestrujesz tę klasę bez konieczności posiadania fabryki dla danych środowiska wykonawczego? Eksperymentowałem z tworzeniem oddzielnych pojemników dla każdego cyklu życia, ale wciąż staram się łączyć różne elementy, gdy obawy dotyczą całego okresu życia. –

+0

W tym przypadku .NET/C# jednak tak naprawdę jestem zainteresowany tym, jak ludzie rozwiązują ten problem projektowy bardziej ogólnie. W szczególności nie chcę polegać zbytnio na danym kontenerze, ponieważ może nie być dostępny na każdej platformie, na którą kieruje. –

Odpowiedz

0

Here to przykład MKOl, który może pomóc. Projekt nazywa się IOC-with-Ninject. Używa Ninject oraz klasy kontenerowej IOC do zarządzania wszystkimi okresami życia obiektu. Będziesz musiał zrobić trochę research on Ninject, aby dostosować go do swoich potrzeb, ale to jest twoje rozwiązanie kontenera IOC (IMHO), jeśli korzystasz z .NET i pomoże ci uporządkować bazę kodów. To osobisty wybór, ale przysięgam na to. Jeśli nie korzystasz z .NET, nadal da ci łatwy wzór do naśladowania. Twoje zdrowie.

0

Wiele kontenerów IoC ma niestandardowe zakresy cyklu życia, którymi można zarządzać zgodnie z własnymi preferencjami. Na przykład w Ninject można zdefiniować niestandardowy zakres cyklu życia, co następuje:

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

Dopóki zmienna yourCustomeScope nie uległa zmianie, jeden instancja obiektu Usługa jest zwracana za każdym razem, gdy jądro przyjmuje wniosek o IService . Gdy tylko zmienna yourCustomeScope ulegnie zmianie, zostanie utworzone nowe wystąpienie usługi przy następnym żądaniu dla IService. yourCustomeScope może być bieżącą instancją odtwarzacza, obiektem gry lub inną rzeczą, która ma zmienić okres istnienia obiektu usługi na podstawie jego zmiany referencyjnej.

Jednak obiekty, które właśnie wymieniłeś, są bardziej prawdopodobne, że są jednostkami, a nie usługami, dla których nie uważam, że wstrzyknięcie jest dobrym pomysłem.

0

Z mojego doświadczenia wynika, że ​​podejście do fabryk działa najlepiej.

Kontrolowanie długości życia instancji jest nieistotne dla wsparcia i wymaga wysiłku, znajomości wszystkich wymagań dotyczących długości życia klas i zależności, czasu konfiguracji i zarządzania konfiguracją. W tym samym czasie użycie fabryk jest naturalne i specyficzne dla kodu.

Tworzenie fabryk (implementacja) można uniknąć, używając proxy factories. Możesz także mieć fabryki zwracające ogólne argumenty, aby jeszcze bardziej zmniejszyć zapotrzebowanie na tworzenie fabryk (interfejsów).

Jeśli wciąż jest zbyt wiele fabryk, sugeruję przejrzenie przepływu kodu.

0

Sądzę, że jest to po części reakcja na niektóre z komentarzy z poprzednich odpowiedzi, ale starałem się zilustrować rozszerzenie niektórych z nich.

Po przejściu do domeny zarządzania długością obiektów wstrzykniętych, prawdopodobnie powinieneś tworzyć fabryki dla tych obiektów.

Podstawowym problemem jest to, że główny składnik kompozycji nie jest świadomy, jaki będzie kontekst środowiskowy wywołania, który musi utworzyć obiekt.

Myślę, że powinienem zrobić krok do tyłu i wyjaśnić w tym miejscu.

Otrzymana mądrość na temat wstrzyknięcia zależnego polega na tym, że w pobliżu punktu wejścia kodu znajduje się pierwiastek kompozycji. Jest wiele dobrych powodów, które nie są trudne do znalezienia w sieci, więc nie będę tutaj wchodził.

Korzeń kompozycji to miejsce, w którym mapujesz interfejsy (zazwyczaj, ale możliwe obiekty) do swoich implantów. Możesz przekazać informacje, które są dostępne w tym momencie do konstruktora. Tak więc możesz przekazać odwołanie do obiektu, którego żywotność jest aktualna w momencie wykonywania głównego katalogu kompozycji.

Jednak, jeśli czas życia katalogu głównego kompozycji nie pokrywa się z czasem życia obiektu, który chcesz utworzyć, musisz odroczyć wykonanie konstruktora, dopóki obiekt nie zostanie utworzony. Dlatego musisz mieć fabrykę. W tym momencie możesz przekazać metodę fabryczną do swojego odwzorowania, a tym samym przekazać informacje potrzebne do wygenerowania obiektu, ale pozwolić na utworzenie w momencie, gdy jest to wymagane, a nie podczas wykonywania głównego katalogu kompozycji.

Nie potrzebujesz klasy fabrycznej, aby to zrobić, metody fabryczne są w porządku, ponadto metoda fabryczna może być wbudowana, więc obciążenie kodu nie jest dużo większe, niż gdybyśmy tworzyli obiekty na trasie kompozycji.

Jeśli mamy projekt z 2 usługami, w których pierwsza usługa jest zależna od pierwszej i chcemy tylko, aby czas życia drugiej usługi był uruchamiany, gdy tworzymy pierwszą usługę, możemy mieć coś takiego. (Używam ninject dać przykład kodu, ale spodziewam się, że pozostałe pojemniki MKOl działają podobnie w tym zakresie).

 

` 
    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>()); 
      } 
     } 
    } 

` 

Ten przykład nie odnosi się w jaki sposób zarządzać żywotność App , czas życia graczy i gier, ale mam nadzieję, że daje on wystarczające wskazówki, jak usunąć problemy z życia związane z zastrzykiem uzależnienia.

Uwaga boczna: w przypadku korzystania z programu Ninject użytkownik może zmienić zakres usługi Service2, aby zarządzać czasem jego istnienia, tak aby trwał przez cały okres użytkowania usługi Service1. Na przykład, jeśli wiesz, że każde wystąpienie gry miało się zdarzyć w jego własnym wątku (OK, może to być może trochę nieprawdopodobne), możesz użyć InThreadScope do gry.

Powiązane problemy