¿Cuál es la diferencia entre XNA Game Services y las variables globales glorificadas?

10

La Microsoft.Xna.Framework.Gameclase tiene una propiedad de Servicios que permite al programador agregar un servicio a su juego al proporcionar el tipo de la clase y una instancia de la clase al método Agregar.

Ahora, en lugar de tener que pasar un AudioComponenta todas las clases y métodos que lo requieren, simplemente pasa su Gameinstancia y busca el servicio. ( Localizador de servicio )

Ahora, debido a que los juegos tienen muchos servicios (GraphicsDevice, SceneGraph, AudioComponent, EffectsManager, etc.), básicamente pasarás el Juego a todo.

Entonces, ¿por qué no hacer de estas instancias un Singleton? Bueno, porque los Singleton son malos porque tienen un estado global, evitan las pruebas y hacen que su diseño sea mucho más frágil. Los localizadores de servicios se consideran igualmente un antipatrón para muchos porque en lugar de simplemente pasar la dependencia a un objeto, se pasa un localizador de servicios (el Juego) que combina esa clase con el resto de los servicios.

Entonces, ¿por qué se recomiendan los servicios en XNA y el desarrollo de juegos? ¿Es porque los juegos son diferentes de los programas normales y están altamente entrelazados con sus componentes y tener que pasar todos los componentes necesarios para el funcionamiento de una clase sería muy engorroso? ¿Son los servicios de juegos en ese momento un mal necesario en el diseño de juegos? ¿Existen alternativas que no impliquen largas listas de parámetros y acoplamiento?

John Pollick
fuente
3
"Los localizadores de servicios se consideran igualmente un antipatrón para muchos" - [cita requerida]. Obviamente, usarlos donde no los necesita es malo, lo cual es cierto para todas las construcciones de software, pero nunca he escuchado que los localizadores de servicios llamen un antipatrón. (Por otro lado, también trato de evitar a los programadores que pasan la mayor parte de su tiempo discutiendo las últimas minucias de patrones).
@JoeWreschnig, aunque estoy de acuerdo con usted, investigué un poco y encontré esto: blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
Roy T.
2
Sí, pero ese tipo está vendiendo un libro sobre el uso de DI; obviamente no quiere que uses algo simple, ¡entonces no comprarás su libro! (Con toda seriedad, el artículo original de DI / SL de Fowler cubrió este tema exacto - martinfowler.com/articles/… - y el comentario del blog de ese tipo no agrega nada a la discusión.)
Desearía poder marcar ese enlace que acabas de publicar de Martin Fowler como respuesta (aunque no es mi pregunta)
Roy T.

Respuestas:

6

No creo que todos estén de acuerdo en que los localizadores de servicios son malos. Los Localizadores de servicios proporcionan una forma de desacoplar funcionalidades importantes que todos necesitan de su implementación real. Los localizadores de servicios también proporcionan una forma de intercambiar servicios en tiempo de ejecución. Además, no creo que sea cierto que deba pasar el localizador de servicios por todas partes. Creo que puedes acceder desde cualquier lugar. Mientras que una clase de juego con globals necesitaría pasarse unas mil veces por cuadro (lo que causaría un mal rendimiento).

Para hacer la diferencia entre el localizador de servicios y una clase con globales un poco más clara, considere el siguiente ejemplo:

Estoy haciendo un juego y uso un objeto de clase gameA que tiene una variable global: el cargador de contenido. Ahora, en mi próximo juego, no quiero usar la clase gameA sino crear una nueva clase gameB. Ahora tengo que volver a agregar los globales del juego A al juego B, que es un trabajo extra. También se hace difícil configurar el tipo de cargador de contenido que quiero. Digamos que tengo un juego que se ejecuta tanto en la PC como en la Xbox. En la PC, quiero un cargador de contenido que también pueda abrir un cuadro de diálogo 'abrir archivo'. Mientras estoy en XBOX definitivamente no quiero esto. Al tener un archivo de configuración, incluso puedo cambiar el cargador de contenido a la mitad del juego, incluso desde otro lugar que no sea la clase gameA, sin hacer públicos todos los globals en gameA.

Ahora considere que estoy usando un localizador de servicios. Siempre puedo seguir usándolo. E incluso qué tipo de servicios hay pueden ser diferentes todo el tiempo. También es cierto que ni siquiera me preocupará su implementación, siempre que tengan una interfaz común. (Aunque también podría usar eso en un gamA como clase).

De todos modos, creo que los servicios del juego te resultarán bastante útiles. Todos los que conozco lo usan. Para ayudarte un poco más. He creado una pequeña clase de ayuda en torno a los servicios de juegos para que puedas echar un poco menos de tiempo todo el tiempo: http://roy-t.nl/index.php/2010/08/25/xna-accessing-contentmanager -and-graphicsdevice-anywhere-anytime-the-gameservicecontainer / Lo uso bastante.

Roy T.
fuente
Gracias por la respuesta. En una clase que requeriría un servicio, ¿normalmente solo solicita la clase GameServices para el servicio cuando sea necesario, o crea una variable miembro en la clase y solicita el servicio solo en el constructor? Supongo que lo que pregunto es qué tan lento return (T)Instance.GetService(typeof(T));es.
John Pollick
Además, ¿qué opinas de esta implementación de la clase GameServices? Eliminé el singleton innecesario porque no tenía ninguna característica que la abstracción no tuviera. Además, eliminé el Servicesufijo redundante al final de cada método.
John Pollick
Además, creo que la razón por la que el AddServicemétodo Servicios toma un tipo es para que pueda especificar la interfaz del servicio. Por ejemplo, Services.AddService(typeof(IGraphicsDeviceManager), graphics);. Luego, al recuperar el servicio, puede especificar la interfaz como el tipo y enviarla a la subclase elegida.
John Pollick
@JohnPollick, para la primera pregunta. Intento solicitarlo siempre (porque podría haber cambiado), pero si realmente necesito algo en cada cuadro, creo una variable miembro. No tengo muchas estadísticas de rendimiento aparte de eso, nunca ha sido un cuello de botella para mí. El segundo, sí, se ve bien :). Y para el tercero. Esa es de hecho la razón del argumento de tipo, sin embargo, nunca debe habituarse a rechazar los valores recuperados porque eso destruye su capacidad de intercambiar clases como servicio. Si no desea un IGraphicsDeviceManger pero MyGDM, posiblemente cree una nueva interfaz.
Roy T.
1
Oh, no te vi también fundido. Por supuesto, puede inferir el tipo si lanzas primero :).
Roy T.
5

Entonces, ¿por qué no hacer de estas instancias un Singleton? Bueno, porque los Singleton son malos porque tienen un estado global, evitan las pruebas y hacen que su diseño sea mucho más frágil.

Los Singletons generalmente toman muchos rasgos útiles y los agrupan en un horrible híbrido que le brinda varias cosas que no desea junto con cualquier parte que quisiera. (¿Fueron características de 'crear a pedido'? ¿Alcance global? ¿Cumplimiento de al menos una instancia? ¿Prohibición de múltiples instancias?)

Los localizadores de servicios se consideran igualmente un antipatrón para muchos porque en lugar de simplemente pasar la dependencia a un objeto, se pasa un localizador de servicios (el Juego) que combina esa clase con el resto de los servicios.

No puedes evitar el acoplamiento por completo. Las cosas usan otras cosas y sin eso su programa no hace nada. Entonces, la única pregunta real es en qué nivel de abstracción se encuentra.

La mayoría de los textos sobre orientación a objetos hacen una distinción entre Composición y Agregación. Es importante conocer la diferencia conceptual entre algo que posee y algo que conoce. Desafortunadamente, la mayoría de los lenguajes modernos no tienen una distinción clara aquí, y una referencia a un objeto podría significar cualquiera de estas cosas. (Irónicamente, C ++ hace un mejor trabajo, si utiliza los distintos tipos de punteros inteligentes).

Una forma en que puede aclarar la composición y la agregación en su código es mediante el uso de servicios para objetos agregados. Los servicios son una forma de proporcionar acceso a cosas que no posee, pero que puede usar.

Entonces, ¿por qué se recomiendan los servicios en XNA y el desarrollo de juegos?

No creo que sean universalmente recomendados en el desarrollo de juegos.

Kylotan
fuente