В чем разница между XNA Game Services и прославленными глобальными переменными?

10

У Microsoft.Xna.Framework.Gameкласса есть свойство Services, которое позволяет программисту добавлять сервис в свою игру, предоставляя тип класса и экземпляр класса методу Add.

Теперь вместо того, чтобы передавать AudioComponentвсе классы и методы, которые в этом нуждаются, вы просто передаете свой Gameэкземпляр и ищите сервис. ( Сервисный Локатор )

Теперь, поскольку в играх много сервисов (GraphicsDevice, SceneGraph, AudioComponent, EffectsManager и т. Д.), Теперь вы будете в основном передавать Game всем.

Так почему бы просто не сделать эти экземпляры Singleton? Ну, потому что Singletons плохи, потому что они имеют глобальное состояние, предотвращают тестирование и делают ваш дизайн намного более хрупким. Локаторы сервисов в равной степени считаются антишаблоном для многих, поскольку вместо простой передачи зависимости объекту вы передаете локатор сервисов (игру), который связывает этот класс с остальными сервисами.

Итак, почему Услуги рекомендуются в XNA и разработке игр? Не потому ли, что игры отличаются от обычных программ и тесно переплетены со своими компонентами, а необходимость проходить через каждый компонент, необходимый для функционирования класса, была бы весьма обременительной? Являются ли Game Services именно тогда необходимым злом в игровом дизайне? Существуют ли альтернативы, которые не включают длинные списки параметров и связь?

Джон Поллик
источник
3
«Сервисные локаторы в равной степени считаются антишаблоном для многих» - [цитата нужна]. Очевидно, что использовать их там, где они вам не нужны, плохо - верно для всех программных конструкций - но я никогда не слышал, чтобы локаторы служб называли анти-паттернами. (С другой стороны, я также стараюсь избегать программистов, которые проводят большую часть своего времени, обсуждая последние подробности паттернов.)
@JoeWreschnig, хотя я с тобой согласен, я немного покопался
Рой Т.
2
Да, но этот парень продает книгу об использовании DI; очевидно, он не хочет, чтобы вы использовали что-то простое, тогда вы не купите его книгу! (На полном серьезе, оригинальная статья Фаулера о DI / SL освещала эту конкретную тему - martinfowler.com/articles/… - и напыщенная речь этого парня ничего не добавляет к обсуждению.)
Хотелось бы пометить ту ссылку, которую вы только что опубликовали от Мартина Фаулера, как ответ (хотя это не мой вопрос)
Рой Т.

Ответы:

6

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

Чтобы немного отличить локатор службы от класса с глобальными переменными, рассмотрим следующий пример:

Я создаю игру и использую объект класса gameA, который имеет глобальную переменную: загрузчик контента. Теперь в моей следующей игре я не хочу использовать класс gameA, а создаю новый класс gameB. Теперь я должен снова добавить глобалы gameA к gameB, что является дополнительной работой. Также становится трудно настроить тип загрузчика контента, который я хочу. Скажем, у меня есть игра, которая работает как на ПК, так и на XBOX. На ПК я хочу загрузчик контента, который также может открыть диалоговое окно «Открыть файл». В то время как на XBOX я определенно не хочу этого. Имея файл конфигурации, я могу даже на полпути через игру легко изменить загрузчик контента, даже где-то еще, кроме класса gameA, не делая общедоступными глобальные переменные в игре.

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

В любом случае, я думаю, что вы найдете игровые сервисы довольно удобными. Все, кого я знаю, используют это. Чтобы помочь вам немного больше. Я создал небольшой вспомогательный класс для игровых сервисов, чтобы вы могли все время выполнять кастинг: http://roy-t.nl/index.php/2010/08/25/xna-accessing-contentmanager -and-graphicsdevice-везде-в-любом-время-сервис-контейнер / Я использую его довольно часто.

Рой Т.
источник
Спасибо за ответ. В классе, который потребует службы, вы обычно запрашиваете класс GameServices для службы, когда это необходимо, или вы создаете переменную-член в классе и запрашиваете службу только в конструкторе? Я предполагаю, что я спрашиваю, насколько медленно return (T)Instance.GetService(typeof(T));.
Джон Поллик
Кроме того, что вы думаете об этой реализации класса GameServices? Я удалил ненужный синглтон, потому что в нем не было функций, которых у абстракции еще не было. Кроме того, я удалил избыточный Serviceсуффикс в конце каждого метода.
Джон Поллик
Кроме того, я думаю, что причина, по которой AddServiceметод Services принимает тип, заключается в том, что вы можете указать интерфейс службы. Например, Services.AddService(typeof(IGraphicsDeviceManager), graphics);. Затем при получении сервиса вы можете указать интерфейс в качестве типа и привести его к выбранному вами подклассу.
Джон Поллик
@JohnPollick, за первый вопрос. Я стараюсь всегда запрашивать его (потому что он мог измениться), но если мне действительно нужно что-то в каждом кадре, я создаю переменную-член. У меня мало статистики о производительности, кроме того, что это никогда не было для меня узким местом. Во-вторых, да, это выглядит хорошо :). И для третьего. Это действительно причина для аргумента типа, однако вы никогда не должны иметь привычку понижать полученные значения, потому что это разрушает вашу способность менять классы как сервис. Если вам нужен не IGraphicsDeviceManger, а MyGDM, возможно, создайте новый интерфейс.
Рой Т.
1
О, я не видел, чтобы ты тоже был кастом. Конечно, он может определить тип, если вы сыграете первым :).
Рой Т.
5

Так почему бы просто не сделать эти экземпляры Singleton? Ну, потому что Singletons плохи, потому что они имеют глобальное состояние, предотвращают тестирование и делают ваш дизайн намного более хрупким.

Синглтоны обычно берут много полезных черт и объединяют их в ужасный гибрид, который дает вам несколько вещей, которые вы не хотите, наряду с тем, что вам нужно. (Это были характеристики «создания по требованию»? Глобальный охват? Обеспечение исполнения хотя бы одного экземпляра? Запрещение нескольких экземпляров?)

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

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

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

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

Итак, почему Услуги рекомендуются в XNA и разработке игр?

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

Kylotan
источник