Inyección de dependencia: ¿Debería crear una clase de automóvil para contener todas sus partes?

10

Tengo muchos autos en mi aplicación C ++, todos contenidos dentro de un RaceTrack.

Cada automóvil consta de cientos de partes. Cada parte depende de alguna otra parte o dos.

He leído muchas preguntas SO sobre el libro de DI y Mark Seemann y parece que no debería definir una clase de automóvil solo para guardar partes del automóvil porque todas las partes del automóvil dependerán entre sí y esta sopa de partes es un automóvil. Estoy en lo cierto?

Entonces, cuando coloco todos mis autos de carreras en el RaceTrack, no habrá entidades de automóviles, sino muchas partes de automóviles, dependiendo de cada una de las carreras en la pista. Estoy en lo cierto?

EDITAR:

Durante años estuve programando clases de autos porque era obvio para mí que necesitaba una clase de autos si estoy programando una lógica de autos. Pero con DI no es tan obvio para mí. ¿Todavía me pregunto si es una idiotez para DI no crear una clase de auto si no tengo un rol definido para ello?

Quiero decir, ¿está bien tener un SteeringWheel para conducir, tornillos y tuercas sobre ruedas para el equipo de boxes y todo tipo de otras interfaces divertidas sin tener una instancia que represente un automóvil en su conjunto?

Anton Petrov
fuente

Respuestas:

24

¿De qué sirve un montón de piezas de automóviles sentados en una pista de carreras haciendo alguien?

Si todo lo que hace su clase de auto es sostener las partes del auto, es tan útil como una bolsa húmeda de partes.

Uso

Como conductor, lo que quiero es algo que puedo controlar. Eso responde cuando le pido velocidad. Eso se maneja como si fuera sobre rieles. Eso puede detenerse en un centavo.

Lo que quiero es una clase de auto que sea utilizable. Que puedo decir que haga cosas sin tener que pensar en cómo funciona el carburador. Solo pienso en el acelerador. Cómo están conectados esos no es algo de lo que me preocupe. Eso es abstracción.

Inyección de dependencia

La inyección de dependencia no tiene nada que ver con eso. Cuando conduzco el automóvil no pienso en cómo se construyó. Mientras funcione, no me importa cómo lo armaron.

No, DI es lo que permite a mi equipo de boxes cambiar rápidamente mis neumáticos por unos mejores cuando comienza a llover en la pista. Es bueno poder hacerlo sin tener que subirse a un automóvil completamente diferente.

DI se trata realmente de seguir un principio: uso separado de la construcción.

Un automóvil que puede instalar llantas nuevas a 90 millas por hora puede sonar genial, pero no creo que vaya a ganar ninguna carrera con un accesorio de instalación de llantas.

DI se trata de instalar sus piezas de una manera que permita a su equipo de boxes llegar a ellas. Cuando lo usa newen el mismo lugar donde programa el comportamiento, es como si estuviera soldando el carburador en su lugar. Seguro que una antorcha de acetileno podría eliminarlo, pero considere usar tuercas y tornillos primero.

Eso es lo que es DI. Claro que no es tan fácil como newinventar algo tan pronto como te das cuenta de que lo quieres. En su lugar, debe escribir un código separado que sepa cómo construir su automóvil. Pero seguro que hace que sea más fácil cambiar las cosas más tarde. Y significa que no tiene que arrastrar una planta de ensamblaje de automóviles por la pista con usted.

Construcción

Algo, en algún lugar, tiene que saber si los neumáticos son Goodyear. ¿Dónde, entonces, poner el código de construcción del automóvil? Si no es el auto, ¿entonces el equipo de boxes? No. La pista? No. Todos los que tienen código de comportamiento. Código que debe realizar durante la carrera. La construcción del automóvil debe suceder antes de la carrera en un lugar retirado del código de comportamiento. Mark Seemans llamó a este lugar la raíz de la composición . La mayoría de la gente lo llama principal.

Es un patrón simple. En general, construya el gráfico de objetos y luego llame a un método de comportamiento en un objeto en el gráfico de objetos. Eso es. Este es el ÚNICO lugar donde la construcción y el comportamiento tienen que estar juntos.

Eso no significa que la construcción tenga que ser una pila de código de procedimiento todo dispuesto secuencialmente en main. Usted es libre de usar todas las herramientas en el lenguaje para hacer la construcción. Simplemente no lo mezcles con el comportamiento.

Hacer esto en el lenguaje y no usar un marco DI o un contenedor IoC se llama DI puro . Funciona muy bien. Tiene por mucho tiempo. Solíamos llamarlo simplemente paso de referencia .

Herramientas DI

Lo que una herramienta DI le compra es los detalles de construcción movidos a un lenguaje diferente (xml, json, lo que sea) que obliga a la separación entre construcción y comportamiento. Si no confías en que tus compañeros programadores no lo usen newcuando no deberían, eso puede ser atractivo.

El inconveniente es que es tentador dejar que los detalles de la herramienta DI se extiendan por toda la base del código. A veces infecta la base del código con anotaciones de propiedad. Los ejemplos que proporcionan ciertamente lo alientan. La herramienta tiende a moverse en el espacio del lenguaje hasta que no se puede anunciar el trabajo como un trabajo de programación Java sino como un trabajo de programación Java / Spring.

Criterios de diseño

Durante años estuve programando clases de autos porque era obvio para mí que necesitaba una clase de autos si estoy programando una lógica de autos. Pero con DI no es tan obvio para mí. ¿Todavía me pregunto si es una idiotez para DI no crear una clase de auto si no tengo un rol definido para ello?

Creo que estás aprendiendo sobre abstracción y cambiando cómo decides que se necesita una clase. Eso es bueno. Pero eso no se trata de DI. DI no te ayuda a decidir si necesitas una clase de auto. DI le ayuda a evitar que el automóvil sepa y, por lo tanto, se preocupe, si los neumáticos son neumáticos Goodyear. DI te ayuda a evitar que la pista sepa si los autos están hechos en Japón.

Una de las preguntas más fundamentales en el diseño de software es "¿qué sabe sobre qué?" Eso es lo principal que te muestra un diagrama UML. Cuando renuevas algo que estás alcanzando, es una interfaz con lo concreto a lo que ahora estás atado. El auto ahora debe saber que los neumáticos son Goodyear. Lo que apesta si Michelin quiere patrocinarte.

Evitar hacer eso se llama seguir el principio de inversión de dependencia . Formalmente, un módulo de alto nivel (como la clase de automóvil) no debería depender directamente de los módulos de bajo nivel (como la clase GoodyearTire). Debería depender de una abstracción (como una interfaz de Tire).

Una forma de evitar hacerlo se llama Inversión de control . Aquí el énfasis está en cambiar el flujo de control. ¿Los neumáticos mueven el automóvil o el automóvil mueve los neumáticos? Pensar en esto de la manera correcta nos permite no asociar estáticamente el automóvil y los neumáticos. DI es una forma particular de seguir la Inversión de Control.

Nada de esto te dice si necesitas una clase de auto. Si está programando la "lógica del automóvil", es bueno si hay un lugar para guardarlo en lugar de dispersarlo por todas partes. Simplemente no se deje engañar pensando que la lógica de construcción de automóviles es la misma que la lógica de comportamiento del automóvil, por lo que todo tiene que vivir en el mismo lugar. Pero si no tiene un rollo definido para un automóvil, tampoco lo necesita. Corre motos por la pista si quieres.

Quiero decir, ¿está bien tener un SteeringWheel para conducir, tornillos y tuercas sobre ruedas para el equipo de boxes y todo tipo de otras interfaces divertidas sin tener una instancia que represente un automóvil en su conjunto?

DI o no DI, está bien tener una instancia que represente un automóvil como un todo, pero esa instancia no es algo que quiera saber directamente si no tengo que hacerlo. Dame una abstracción del auto para que no me importe si funciona con gasolina, diesel o electricidad cuando lo uso. Eso es solo algo que debería importarme cuando lo estoy construyendo o manteniendo. Es bueno si el código que usa el automóvil no tiene que saber o importar cómo funciona. "No sé. No quiero saber".

naranja confitada
fuente
Sería genial si no usaras la metáfora del auto y el equipo de boxes para una pregunta que los use para representar el código. Pero aún así, una buena respuesta.
S. Tarık Çetin
66
Y siempre pensé Inversión de Control fue cuando direccional izquierda, pero el coche gira a la derecha ...
mkrieger1
CandiedOrange, ¿entonces no hay clase de coches si no tiene una interfaz significativa y una responsabilidad bien definida? Y no Car.h en mi proyecto. ¿Y un colega que mira mi código y se pregunta si es una tienda electrónica de repuestos de automóviles o una aplicación de administración de garaje? :)
Anton Petrov
@AntonPetrov "si parece un pato y grazna como un pato, pero necesita baterías, probablemente tenga la abstracción incorrecta". Los buenos nombres son muy importantes y pueden ser difíciles de pensar, pero deben cambiarse para reflejar la responsabilidad bien definida y la interfaz significativa para que no sean una sorpresa. Si leo el nombre, miro la interfaz y pienso "eso es más o menos lo que esperaba", entonces tienes un buen nombre.
candied_orange
15

parece que no debería definir una clase de Automóvil solo para guardar las piezas del automóvil porque todas las piezas del automóvil dependerán entre sí y esta sopa de piezas es un automóvil. Estoy en lo cierto?

No.

El punto de inyección de dependencias no es para desalentar las dependencias. Todo lo contrario.

La inyección de dependencia se trata de la pregunta: ¿de dónde viene el coche conseguir sus partes de ? ¿Dónde y cómo se crean, y cómo obtiene el auto referencias a ellos?

Ingenuamente, el automóvil crearía sus propias partes llamando new. Esto es fácil y directo, pero es muy inflexible. El automóvil tiene que saber todo lo que es necesario para crear las partes, y si alguna vez desea tener dos autos con partes diferentes, esa lógica también debe entrar en el automóvil.

Con la inyección de dependencia, toda esa lógica se saca del automóvil y se coloca en un componente de configuración, que crea todas las partes y conecta las referencias. Las dependencias totalmente configuradas se inyectan en el automóvil, y entre sí.

Michael Borgwardt
fuente
1
Finalmente, una explicación sensata del dependency injectionconcepto.
anatoly techtonik
1
Yo diría que el sistema más común para el enfoque "ingenuo" no es que el automóvil llamaría nuevo, sino que el usuario de la clase de automóvil asignaría sus partes a sus propiedades, y mientras tanto la clase de automóvil estaría en Un estado indeterminado. DI proporciona un medio para asegurar mecánicamente la validez de clase.
whatsisname
2
@whatsisname Puedo usar fácilmente DI para crear objetos inválidos y medio inicializados. La validación no es una responsabilidad de DI. Tampoco es ser inmutable. DI puede asumir el trabajo de construir objetos válidos e inmutables. DI no tiene forma de exigir que esas sean las únicas formas en que esos objetos se construyen y usan. Los objetos deben hacer cumplir esto.
candied_orange
@CandiedOrange: para que pueda usar fácilmente cualquier técnica para hacer algo a medias, ¿y qué? Requerir dependencias en el constructor, y hacerlas inmutables, no es una bala de plata, pero sin embargo es una forma muy útil de prevenir muchas situaciones malas comunes.
whatsisname
0

Entonces, cuando coloco todos mis autos de carreras en el RaceTrack, no habrá entidades de automóviles, sino muchas partes de automóviles, dependiendo de cada una de las carreras en la pista. Estoy en lo cierto?

Bien. Si y no. Una entidad automotriz aún es válida para tener. Y un automóvil es más que la suma de sus partes. También necesita un controlador (por el momento). Muchas veces los autos de carrera también tienen nombres, sin mencionar la marca y el modelo del automóvil.

Sí, los automóviles son básicamente un contenedor de piezas, pero es este embalaje y la cooperación de las piezas lo que conforma algo más grande: un automóvil.

Así que realmente no. Un automóvil no es solo una bolsa aleatoria de metal y plástico. Es una maquina.

La inyección de dependencia no es excesiva en este caso. Algo más tiene que construir el automóvil, que sería una clase de fábrica u objeto de construcción. El automóvil no debería saber cómo construirse solo.

Greg Burghardt
fuente
2
Puede tener DI sin fábricas u objetos de construcción. Los parámetros de constructor simple son un método válido que funciona para muchas circunstancias.
whatsisname