Jaka jest różnica między usługami gier XNA a gloryfikowanymi zmiennymi globalnymi?

10

Microsoft.Xna.Framework.GameKlasa ma Services właściwość, która pozwala programiście dodać usługę do swojej gry poprzez dostarczanie rodzaj klasy i instancję klasy do metody Add.

Teraz zamiast przekazywać AudioComponentdo wszystkich klas i metod, które tego wymagają, wystarczy przekazać Gameinstancję i wyszukać usługę. (Lokalizator usług )

Ponieważ gry mają wiele usług (GraphicsDevice, SceneGraph, AudioComponent, EffectsManager, itd.), Teraz zasadniczo będziesz przekazywać grę do wszystkiego.

Dlaczego więc nie uczynić z tych przypadków singletonów? Cóż, ponieważ Singletony są złe, ponieważ mają stan globalny, zapobiegają testowaniu i sprawiają, że projekt jest znacznie bardziej kruchy. Lokalizatory usług są równie uważane za anty-wzorzec dla wielu, ponieważ zamiast przekazywać zależność od obiektu, podajesz lokalizator usług (Gra), który łączy tę klasę z resztą usług.

Dlaczego więc Usługi są zalecane w XNA i tworzeniu gier? Czy to dlatego, że gry różnią się od zwykłych programów i są ściśle powiązane ze swoimi komponentami, a konieczność przejścia każdego komponentu niezbędnego do funkcjonowania klasy byłaby bardzo uciążliwa? Czy usługi gier są zatem niezbędnym złem w projektowaniu gier? Czy istnieją alternatywy, które nie wymagają długich list parametrów i łączenia?

John Pollick
źródło
3
„Lokalizatory usług są w równym stopniu uważane za anty-wzór dla wielu” - [potrzebne źródło]. Oczywiście używanie ich tam, gdzie ich nie potrzebujesz, jest złe - dotyczy wszystkich konstrukcji oprogramowania - ale nigdy nie słyszałem, aby lokalizatory usług nazywane były anty-wzorcem. (Z drugiej strony staram się także unikać programistów, którzy spędzają większość czasu na omawianiu najnowszych drobiazgów wzorców.)
@JoeWreschnig, chociaż się z tobą zgadzam, zrobiłem trochę kopania i znalazłem to: blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
Roy T.
2
Tak, ale ten facet sprzedaje książkę o używaniu DI; oczywiście nie chce, żebyś używał czegoś prostego, wtedy nie kupisz jego książki! (Z całą powagą oryginalny artykuł Fowlera na temat DI / SL opisał dokładnie ten temat - martinfowler.com/articles/... - a blog tego faceta nic nie dodaje do dyskusji.)
Chciałbym oznaczyć ten link, który właśnie przesłałeś Martin Fowler jako odpowiedź (chociaż to nie moje pytanie)
Roy T.,

Odpowiedzi:

6

Nie sądzę, aby wszyscy zgadzali się, że lokalizatory usług są złe. Lokalizatory usług zapewniają sposób na oddzielenie ważnej funkcjonalności, której wszyscy potrzebują od ich rzeczywistej implementacji. Lokalizatory usług umożliwiają także wymianę usług w czasie wykonywania. Nie sądzę też, że to prawda, że ​​musisz wszędzie wokół znaleźć lokalizator usług. Myślę, że możesz uzyskać do niego dostęp z dowolnego miejsca. Podczas gdy klasa gry z globalsami musiałaby być przekazywana około tysiąca razy na klatkę (co spowodowałoby złą wydajność).

Aby nieco rozróżnić lokalizator usług od klasy z globalsami, rozważ następujący przykład:

Tworzę grę i używam obiektu klasy Game, który ma zmienną globalną: moduł ładujący zawartość. Teraz w mojej następnej grze nie chcę korzystać z klasy gameA, ale tworzę nową klasę gameB. Muszę teraz ponownie dodać globały gameA do gameB, co jest dodatkową pracą. Również staje się trudne skonfigurowanie typu modułu ładującego treści, który chcę. Powiedzmy, że mam grę, która działa zarówno na PC, jak i na XBOX. Na komputerze chcę moduł ładujący treści, który może również otworzyć okno dialogowe „otwórz plik”. Będąc na XBOX zdecydowanie tego nie chcę. Mając plik konfiguracyjny, mogę nawet w połowie gry łatwo zmienić moduł ładujący zawartość, nawet skądinąd klasy GameA, bez upubliczniania globałów w gameA.

Rozważmy teraz, że używam lokalizatora usług. Zawsze mogę go używać. Nawet rodzaje usług mogą być różne przez cały czas. Prawdą jest również to, że nie będę nawet dbał o ich implementację, o ile mają one wspólny interfejs. (Chociaż mógłbym użyć tego również w klasie typu gamA).

W każdym razie myślę, że usługi gier będą dla ciebie bardzo przydatne. Wszyscy, których znam, używają tego. Aby ci trochę pomóc. Stworzyłem małą klasę pomocników wokół usług gier, dzięki czemu możesz cały czas robić o wiele mniej: http://roy-t.nl/index.php/2010/08/25/xna-accessing-contentmanager -i-grafika-urządzenie-gdziekolwiek-kiedykolwiek-gra-serwisowy pojemnik / Używam go dość często.

Roy T.
źródło
Dziękuję za odpowiedź. Czy w klasie, która wymagałaby usługi, zazwyczaj po prostu żądasz klasy GameServices dla usługi, gdy jest to potrzebne, czy tworzysz zmienną składową w klasie i żądasz usługi tylko w konstruktorze? Przypuszczam, że pytam o to, jak powolny return (T)Instance.GetService(typeof(T));.
John Pollick
Co sądzisz o tej implementacji klasy GameServices? Usunąłem niepotrzebny singleton, ponieważ nie miał żadnych cech, których abstrakcja jeszcze nie miała. Ponadto usunąłem zbędny Servicesufiks na końcu każdej metody.
John Pollick,
Myślę też, że powodem, dla którego AddServicemetoda Services przyjmuje typ, jest to, że możesz określić interfejs usługi. Na przykład Services.AddService(typeof(IGraphicsDeviceManager), graphics);. Następnie podczas pobierania usługi możesz określić interfejs jako typ i rzutować go na wybraną podklasę.
John Pollick
@JohnPollick, na pierwsze pytanie. Staram się zawsze o to poprosić (ponieważ mógł się zmienić), ale jeśli naprawdę potrzebuję czegoś w każdej ramce, tworzę zmienną składową. Nie mam wielu statystyk wydajności poza tym, że nigdy nie było to dla mnie wąskim gardłem. Drugi, tak, wygląda dobrze :). I po trzecie. To jest rzeczywiście powód argumentu typu, jednak nigdy nie powinieneś nawiązywać do pobierania wartości pobranych, ponieważ niweczy to twoją zdolność do zamiany klas jako usługi. Jeśli nie chcesz IGraphicsDeviceManger, ale MyGDM, prawdopodobnie stwórz nowy interfejs.
Roy T.
1
Och, nie widziałem, żebyś także castował. Oczywiście jest w stanie wywnioskować rodzaj, jeśli rzucisz pierwszy :).
Roy T.
5

Dlaczego więc nie uczynić z tych przypadków singletonów? Cóż, ponieważ Singletony są złe, ponieważ mają stan globalny, zapobiegają testowaniu i sprawiają, że projekt jest znacznie bardziej kruchy.

Singletony na ogół mają wiele przydatnych cech i łączą je razem w okropną hybrydę, która daje ci kilka rzeczy, których nie chcesz wraz z tym, co chcesz. (Czy miało to charakterystykę tworzenia na żądanie? Zakres globalny? Egzekwowanie co najmniej jednej instancji? Zakaz wielu instancji?)

Lokalizatory usług są równie uważane za anty-wzorzec dla wielu, ponieważ zamiast przekazywać zależność od obiektu, podajesz lokalizator usług (Gra), który łączy tę klasę z resztą usług.

Nie można całkowicie uniknąć sprzężenia. Rzeczy używają innych rzeczy, a bez tego twój program nic nie robi. Tak więc jedynym prawdziwym pytaniem jest, na jakim poziomie abstrakcji jest.

Większość tekstów o orientacji obiektowej rozróżnia kompozycję i agregację. Ważne jest, aby znać koncepcyjną różnicę między czymś, co posiadasz, a czymś, o czym wiesz. Niestety większość współczesnych języków nie ma tutaj wyraźnego rozróżnienia, a odniesienie do obiektu może oznaczać jedną z tych rzeczy. (Jak na ironię, C ++ działa lepiej, jeśli używasz różnych inteligentnych typów wskaźników).

Jednym ze sposobów, dzięki którym można wyczyścić skład i agregację w kodzie, jest użycie usług dla obiektów agregowanych. Usługi to jeden ze sposobów zapewnienia dostępu do rzeczy, których nie posiadasz, ale z których możesz korzystać.

Dlaczego więc Usługi są zalecane w XNA i tworzeniu gier?

Nie sądzę, że są one powszechnie zalecane w tworzeniu gier.

Kylotan
źródło