¿Los juegos minoristas usan "inversión de control" e "inyección de dependencia"?

30

Muchos de los desarrolladores de software más diligentes que conozco se están moviendo hacia la inversión de control y la inyección de dependencia para manejar referencias a objetos. Desde la perspectiva de los juegos Flash, no conozco todos los entresijos de los estudios AAA, entonces: ¿se usan en el mundo de los juegos minoristas?

Iain
fuente
Creo que en ese mundo el rendimiento es importante sobre todo, ya que más usuarios se verán afectados por el bajo rendimiento que los programadores por un mal código.
Bart van Heukelom el
La inyección de dependencia suena como un sistema basado en componentes, pero ¿tendría un ejemplo de cómo se podría invertir el control?
ADB
Como la mayoría de este hilo parece estar mal informado sobre qué es IoC y qué resuelve, y el OP realmente no está pidiendo que, en primer lugar, apunte aquí para futuros visitantes: sebaslab.com/ioc-container-unity-part-1
Boris Callens
Ellos si. De hecho, Rare habló sobre cómo están implementando sus pruebas en Sea of ​​Thieves: youtube.com/watch?v=KmaGxprTUfI&t=1s . Desafortunadamente, no tenían mucho que decir sobre la Inversión de control en Unreal Engine, sin embargo, he escrito un proyecto que demuestra cómo funciona: github.com/jimmyt1988/UE-Testing
Jimmyt1988

Respuestas:

45

Lo llamas "desarrollo de software diligente", lo llamo "ingeniería excesiva dolorosa". Eso no quiere decir que la inversión de control sea mala, de hecho, la definición básica de la misma es buena, pero la proliferación de marcos y métodos de trabajo completos para lograr todo esto es poco menos que una locura, especialmente combinada con la forma en que la gente está destrozando Interfaces perfectamente buenas y limpias para poder inyectar componentes intercambiables que el 99% del tiempo nunca intercambiarás. Es el tipo de cosas que solo pueden originarse en el entorno empresarial de Java y me alegro de que no tenga tanta influencia en otros lugares.

A menudo, el argumento es que, incluso si no intercambia componentes, desea poder probarlos de forma aislada con objetos simulados y similares. Sin embargo, me temo que nunca compraré el argumento de que vale la pena hinchar y complicar una interfaz para poder probarla mejor. Las pruebas prueban una sola cosa: que sus pruebas funcionen. Las interfaces limpias y mínimas, por otro lado, ayudan a demostrar que su código funciona.

Entonces, la respuesta corta: sí, pero no en la forma en que piensas. A veces, cuando necesita un comportamiento intercambiable, pasará un objeto a un constructor que dicta parte del comportamiento del nuevo objeto. Eso es todo.

Kylotan
fuente
55
+1 - Estoy de acuerdo en que la comunidad Java parece haber complicado demasiado lo que parece ser una idea muy simple.
Chris Howe
44
La idea no es necesariamente que deba intercambiar diferentes implementaciones en producción, sino que tal vez desee inyectar implementaciones especiales para facilitar las pruebas automatizadas. En ese sentido, DI / IoC ciertamente puede ser útil en los juegos, pero desea mantener un alcance razonable. No deberías necesitar más que unas pocas resoluciones de servicio únicas a un alto nivel: no quieres resolver servicios para cada pequeño objeto en el juego, y ciertamente no para cada cuadro o algo así.
Mike Strobel el
2
@Thomas Dufour: el concepto de una prueba nunca puede probar nada. Es solo un punto de datos. Puede pasar una prueba 100000 veces sin probar que pasará en la ejecución 100001. La prueba proviene del análisis lógico del tema. Yo diría que estos contenedores de IoC y similares facilitan las pruebas a expensas de dificultar la comprobación de la corrección, y creo que eso es algo malo.
Kylotan
13
@Kylotan el concepto de una prueba nunca intenta probar nada. Intenta demostrar que el comportamiento con las entradas dadas es el esperado. Cuanto más se demuestre que el comportamiento es correcto, mayor será su creencia de que la función general es correcta pero nunca es del 100%. La afirmación de que una interfaz mínima demuestra que cualquier cosa es ridícula.
dash-tom-bang
55
@Alex Schearer: Me temo que nunca puedo aceptar que la necesidad de usar un sistema de contenedor IoC formalizado y, por lo general, crear argumentos de constructor adicionales o clases de fábrica para facilitar la prueba, de alguna manera, sea un factor mejor, y ciertamente no haciéndolo más flojo acoplado. De hecho, prácticamente pisotea la encapsulación, un aspecto clave de la POO. En lugar de confiar en TDD para impulsar su diseño y esperar que surja un buen resultado, la gente podría seguir los principios SOLID en primer lugar.
Kylotan
17

Patrón de estrategia , composición , inyección de dependencia , están todos muy relacionados.

Dado que el Patrón de estrategia es una forma de inyección de dependencia, si observa motores como Unity, por ejemplo, se basan completamente en este principio. Su uso de Componentes (Patrón de Estrategia) está profundamente incrustado en todo su motor.

Uno de los principales beneficios, aparte de la reutilización de componentes, es evitar las temidas jerarquías de clases profundas.

Aquí hay un artículo de Mick West que habla sobre cómo introdujo este tipo de sistema en la serie de juegos Tony Hawk de Neversoft.

Evoluciona tu jerarquía

Hasta años relativamente recientes, los programadores de juegos han usado constantemente una jerarquía de clase profunda para representar las entidades del juego. La marea está comenzando a cambiar de este uso de jerarquías profundas a una variedad de métodos que componen un objeto de entidad de juego como una agregación de componentes ...

David Young
fuente
2
+1, Evolve Your Hierarchy es un gran enlace del que me había olvidado por completo. Las diapositivas de Scott Bilas también son buenas; el enlace correcto para estas es ( scottbilas.com/files/2002/gdc_san_jose/game_objects_slides.pdf ). Hay un conjunto más de diapositivas relacionadas, pero he olvidado dónde ...
leander
También el artículo en Games Gems 5 (creo) de Bjarne Rene.
Chris Howe
13

Parece haber mucha confusión sobre el patrón de Inversión de Control (IoC). Varias personas lo han equiparado con el Patrón de estrategia o un Modelo de componentes, pero esta comparación realmente no capta de qué se trata IoC. IoC realmente trata sobre cómo se obtiene una dependencia. Dejame darte un ejemplo:

class Game {
    void Load() {
        this.Sprite.Load(); // loads resource for drawing later
    }
}

class Sprite {
    void Load() {
        FileReader reader = new FileReader("path/to/resource.gif");
        // load image from file
    }
}

En lo anterior está claro que Sprite.Loadtiene una dependencia de a FileReader. Cuando desee probar el método, necesita lo siguiente:

  1. Un sistema de archivos en su lugar
  2. Un archivo de prueba para cargar desde el sistema de archivos
  3. Capacidad para desencadenar errores comunes del sistema de archivos

Los dos primeros son obvios, pero si desea asegurarse de que su manejo de errores funcione como se espera, realmente también necesita el n. ° 3. En ambos casos, posiblemente haya ralentizado sus pruebas un poco, ya que ahora deben ir al disco y probablemente haya complicado su entorno de prueba.

El objetivo de IoC es desacoplar el uso del comportamiento de su construcción. Observe cómo esto difiere del patrón de Estrategia. Con el patrón de estrategia, el objetivo es encapsular una parte de comportamiento reutilizable para que pueda ampliarla fácilmente en el futuro; No tiene nada que decir sobre cómo se construyen las estrategias.

Si tuviéramos que reescribir el Sprite.Loadmétodo anterior, probablemente terminaríamos con:

class Sprite {
    void Load(IReader reader) {
        // load image through reader
    }
}

Ahora, hemos desacoplado la construcción del lector de su uso. Por lo tanto, es posible intercambiar un lector de prueba durante la prueba. Esto significa que su entorno de prueba ya no necesita un sistema de archivos, archivos de prueba y puede simular fácilmente eventos de error.

Tenga en cuenta que hice dos cosas en mi reescritura. Creé una interfaz IReaderque encapsulaba algún comportamiento, es decir, implementé el patrón de Estrategia. Además, trasladé la responsabilidad de crear el lector adecuado a otra clase.

Tal vez no necesitamos un nuevo nombre de patrón para describir lo anterior. Me parece una combinación de los patrones de Estrategia y Fábrica (para contenedores IoC). Dicho esto, no estoy seguro de por qué las personas se oponen a este patrón, ya que está claro que resuelve un problema real y, ciertamente, no es obvio para mí lo que esto tiene que ver con Java.

Alex Schearer
fuente
2
Alex: nadie se opone a pasar objetos ya creados para impulsar la funcionalidad personalizada donde sea necesario. Todo el mundo lo hace, como en tu ejemplo. El problema es cuando las personas usan marcos completos que existen únicamente para manejar estas cosas y codifican religiosamente cada aspecto de la funcionalidad para depender de esta funcionalidad. Primero agregan IReader como parámetro para la construcción de Sprite, y luego terminan necesitando objetos especiales SpriteWithFileReaderFactory para facilitar esto, etc. Esta actitud se origina en Java y cosas como los contenedores Spring IoC, etc.
Kylotan
2
Pero no estamos hablando de contenedores IoC, al menos no lo veo en la publicación original. Solo estamos hablando del patrón en sí. Su argumento, lo mejor que puedo decir, es "Debido a que algunas herramientas en la naturaleza son malas, no deberíamos usar los conceptos subyacentes". Eso me parece arrojar al bebé con el agua del baño. Lograr el equilibrio correcto entre la simplicidad, hacer las cosas y la mantenibilidad / comprobabilidad es un problema difícil que probablemente se trate mejor proyecto por proyecto.
Alex Schearer
Sospecho que muchas personas están en contra de la COI, ya que significa:
phtrivier
4

Yo diría que es una herramienta entre muchas, y se usa en ocasiones. Como dijo tenpn, cualquier método que introduzca vtables (y generalmente cualquier indirección adicional) puede tener una penalización de rendimiento, pero esto es algo de lo que solo debemos preocuparnos por el código de bajo nivel. Para el código estructural de alto nivel que realmente no es un problema, y ​​la IoC puede tener beneficios positivos. Cualquier cosa para reducir las dependencias entre clases y hacer que su código sea más flexible.

Chris Howe
fuente
2
Para ser precisos, me preocuparía por las penalizaciones de vtable para cualquier código que se llame muchas veces un marco. Esto puede o no ser de bajo nivel.
Tenpn
4

Tengo que estar de acuerdo con Kylotan en este caso. La "inyección de dependencia" es una solución fea de Java para un defecto conceptual feo de Java, y la única razón por la que alguien lo está mirando en otros idiomas es porque Java se está convirtiendo en el primer idioma para mucha gente, cuando realmente no debería.

La inversión del control, por otro lado, es una idea útil que ha existido durante mucho tiempo y es muy útil cuando se hace correctamente. (No es la forma de inyección de Java / Dependencia). De hecho, probablemente lo haga todo el tiempo si está trabajando en un lenguaje de programación sensato. Cuando leí por primera vez todo este asunto del "COI" sobre el que todos hablaban, me sentí completamente decepcionado. La inversión de control no es más que pasar un puntero de función (o puntero de método) a una rutina u objeto para ayudar a personalizar su comportamiento.

Esta es una idea que existe desde la década de 1950. Está en la biblioteca estándar de C (qsort viene a la mente) y está en todas partes en Delphi y .NET (controladores / delegados de eventos). Permite que el código antiguo llame a código nuevo sin necesidad de volver a compilar el código antiguo, y se usa todo el tiempo en los motores de juego.

Mason Wheeler
fuente
2
Yo también era de "¿así que esto es lo que entusiasma a la gente?" cuando leí sobre DI, pero el hecho es que la mayoría de los programadores de juegos podrían aprenderlo y cómo se aplicaría al trabajo que hacemos.
dash-tom-bang
2

No en mi experiencia. Lo cual es una pena, ya que el código de los juegos debe ser muy adaptable, y estos enfoques definitivamente ayudarían.

Sin embargo, debe decirse que ambos métodos podrían, en C ++, introducir tablas virtuales donde no las había antes, trayendo consigo un éxito adecuado en el rendimiento.

tenpn
fuente
1

No soy un desarrollador profesional de juegos, y solo intenté implementar IoC en C ++ una vez, así que esto es solo especulación.

Sin embargo, sospecho que los desarrolladores de juegos sospecharían de IoC porque significa:

1 / diseño de muchas interfaces y muchas clases pequeñas

2 / teniendo casi todas las llamadas a funciones vinculadas últimamente

Ahora, puede ser una coincidencia, pero ambos tienden a ser un poco más complicados y / o problemáticos para las actuaciones en C ++ (que se usa ampliamente en el juego, ¿no?) Que en Java, donde IoC se generalizó (probablemente porque era relativamente fácil de hacer, ayudar a las personas a diseñar jerarquías de objetos más sanas, y porque algunos creían que podría convertir escribir una aplicación en Java en escribir una aplicación en XML, pero ese es otro debate: P)

Me interesan los comentarios si eso no tiene ningún sentido;)

phtrivier
fuente