Si entiendo correctamente, el mecanismo típico para la Inyección de dependencias es inyectar a través de un constructor de clase o de una propiedad pública (miembro) de la clase.
Esto expone la dependencia que se inyecta y viola el principio de encapsulación OOP.
¿Estoy en lo correcto al identificar esta compensación? ¿Cómo lidias con este problema?
Consulte también mi respuesta a mi propia pregunta a continuación.
Respuestas:
Hay otra forma de ver este problema que puede resultarle interesante.
Cuando usamos IoC / inyección de dependencia, no estamos usando conceptos OOP. Es cierto que estamos usando un lenguaje OO como 'host', pero las ideas detrás de IoC provienen de la ingeniería de software orientada a componentes, no OO.
El software de componentes se trata de administrar dependencias; un ejemplo de uso común es el mecanismo de ensamblaje de .NET. Cada conjunto publica la lista de conjuntos a los que hace referencia, y esto hace que sea mucho más fácil reunir (y validar) las piezas necesarias para una aplicación en ejecución.
Al aplicar técnicas similares en nuestros programas OO a través de IoC, nuestro objetivo es hacer que los programas sean más fáciles de configurar y mantener. Publicar dependencias (como parámetros de constructor o lo que sea) es una parte clave de esto. La encapsulación realmente no se aplica, ya que en el mundo orientado a componentes / servicios, no hay un 'tipo de implementación' para que se filtren los detalles.
Desafortunadamente, nuestros lenguajes actualmente no segregan los conceptos orientados a objetos de grano fino de los orientados a componentes de grano más grueso, por lo que esta es una distinción que solo debe tener en mente :)
fuente
Es una buena pregunta, pero en algún momento, la encapsulación en su forma más pura debe ser violada si alguna vez se cumple la dependencia del objeto. Algunos proveedores de la dependencia deben saber que el objeto en cuestión requiere una
Foo
y que el proveedor debe tener una forma de proporcionarlaFoo
al objeto.Clásicamente este último caso se maneja como usted dice, a través de argumentos de constructor o métodos de establecimiento. Sin embargo, esto no es necesariamente cierto: sé que las últimas versiones del marco Spring DI en Java, por ejemplo, le permiten anotar campos privados (por ejemplo, con
@Autowired
) y la dependencia se establecerá a través de la reflexión sin necesidad de exponer la dependencia a través de cualquiera de las clases métodos / constructores públicos. Este podría ser el tipo de solución que estaba buscando.Dicho esto, tampoco creo que la inyección de constructor sea un gran problema. Siempre he pensado que los objetos deberían ser completamente válidos después de la construcción, de modo que cualquier cosa que necesiten para desempeñar su función (es decir, estar en un estado válido) debería ser suministrada a través del constructor de todos modos. Si tiene un objeto que requiere un colaborador para trabajar, me parece bien que el constructor publique públicamente este requisito y se asegure de que se cumpla cuando se cree una nueva instancia de la clase.
Idealmente cuando se trata con objetos, interactúa con ellos a través de una interfaz de todos modos, y cuanto más lo haga (y tenga dependencias conectadas a través de DI), menos tendrá que lidiar con los constructores usted mismo. En la situación ideal, su código no trata ni crea instancias concretas de clases; así que solo se le da un
IFoo
DI directo, sin preocuparse de lo que el constructor deFooImpl
indica que necesita para hacer su trabajo, y de hecho sin siquiera darse cuenta deFooImpl
la existencia. Desde este punto de vista, la encapsulación es perfecta.Esta es una opinión, por supuesto, pero en mi opinión, DI no necesariamente viola la encapsulación y, de hecho, puede ayudarlo al centralizar todo el conocimiento necesario de los componentes internos en un solo lugar. No solo es algo bueno en sí mismo, sino que incluso mejor este lugar está fuera de su propia base de código, por lo que ninguno de los códigos que escriba necesita saber sobre las dependencias de las clases.
fuente
Bueno, francamente hablando, todo viola la encapsulación. :) Es una especie de principio tierno que debe tratarse bien.
Entonces, ¿qué viola la encapsulación?
La herencia sí .
La programación orientada a aspectos sí . Por ejemplo, registra la devolución de llamada onMethodCall () y eso le brinda una gran oportunidad para inyectar código en la evaluación del método normal, agregar efectos secundarios extraños, etc.
La declaración de amigo en C ++ sí .
La extensión de clase en Ruby sí . Simplemente redefina un método de cadena en algún lugar después de que una clase de cadena se haya definido completamente.
Bueno, muchas cosas sí .
La encapsulación es un principio bueno e importante. Pero no el único.
fuente
Sí, DI viola la encapsulación (también conocida como "ocultación de información").
Pero el verdadero problema surge cuando los desarrolladores lo usan como una excusa para violar los principios de KISS (Keep It Short and Simple) y YAGNI (You Don't Don't Need You Need It).
Personalmente, prefiero soluciones simples y efectivas. Principalmente uso el "nuevo" operador para crear instancias de dependencias con estado cuando y donde se necesiten. Es simple, bien encapsulado, fácil de entender y fácil de probar. ¿Entonces por qué no?
fuente
Un buen contenedor / sistema de inyección de confianza permitirá la inyección del constructor. Los objetos dependientes se encapsularán y no necesitan exponerse públicamente. Además, mediante el uso de un sistema DP, ninguno de sus códigos incluso "conoce" los detalles de cómo se construye el objeto, posiblemente incluso el objeto que se está construyendo. Hay más encapsulación en este caso, ya que casi todo su código no solo está protegido del conocimiento de los objetos encapsulados, sino que ni siquiera participa en la construcción de los objetos.
Ahora, supongo que está comparando con el caso en el que el objeto creado crea sus propios objetos encapsulados, muy probablemente en su constructor. Entiendo que DP es que queremos quitarle esta responsabilidad al objeto y dársela a otra persona. Con ese fin, el "alguien más", que es el contenedor DP en este caso, tiene un conocimiento íntimo que "viola" la encapsulación; El beneficio es que extrae ese conocimiento del objeto mismo. Alguien tiene que tenerlo. El resto de su aplicación no.
Lo pensaría de esta manera: el contenedor / sistema de inyección de dependencia viola la encapsulación, pero su código no. De hecho, su código está más "encapsulado" que nunca.
fuente
Como Jeff Sternal señaló en un comentario a la pregunta, la respuesta depende completamente de cómo se defina la encapsulación .
Parece que hay dos campos principales de lo que significa la encapsulación:
File
objeto puede tener métodos aSave
,Print
,Display
,ModifyText
, etc.Estas dos definiciones están en contradicción directa entre sí. Si un
File
objeto puede imprimirse, dependerá en gran medida del comportamiento de la impresora. Por otro lado, si simplemente sabe algo que puede imprimir para él (unaIFilePrinter
o alguna de esas interfaces), entonces elFile
objeto no tiene que saber nada sobre impresión, por lo que trabajar con él traerá menos dependencias en el objeto.Entonces, la inyección de dependencia interrumpirá la encapsulación si usa la primera definición. Pero, francamente, no sé si me gusta la primera definición: claramente no escala (si lo hiciera, MS Word sería una gran clase).
Por otro lado, la inyección de dependencia es casi obligatoria si está utilizando la segunda definición de encapsulación.
fuente
No viola la encapsulación. Estás proporcionando un colaborador, pero la clase decide cómo se usa. Mientras sigas a Tell, no preguntes, las cosas están bien. Creo que la inyección de constructor es preferible, pero los colocadores pueden estar bien, siempre y cuando sean inteligentes. Es decir, contienen lógica para mantener los invariantes que representa la clase.
fuente
Esto es similar a la respuesta votada, pero quiero pensar en voz alta, tal vez otros también vean las cosas de esta manera.
OO clásico utiliza constructores para definir el contrato público de "inicialización" para los consumidores de la clase (ocultando TODOS los detalles de implementación; también conocido como encapsulación). Este contrato puede garantizar que, después de la creación de instancias, tenga un objeto listo para usar (es decir, ningún paso de inicialización adicional para ser recordado (er, olvidado) por el usuario).
(constructor) DI sin lugar a dudas rompe la encapsulación al sangrar los detalles de implementación a través de esta interfaz de constructor público. Mientras sigamos considerando al constructor público responsable de definir el contrato de inicialización para los usuarios, hemos creado una violación horrible de la encapsulación.
Ejemplo teórico:
Class Foo tiene 4 métodos y necesita un número entero para la inicialización, por lo que su constructor se parece a Foo (int size) y los usuarios de la clase Foo tienen claro de inmediato que deben proporcionar un tamaño en la instanciación para que Foo funcione.
Digamos que esta implementación particular de Foo también puede necesitar un IWidget para hacer su trabajo. La inyección del constructor de esta dependencia nos haría crear un constructor como Foo (int size, widget de IWidget)
Lo que me molesta de esto es que ahora tenemos un constructor que combina datos de inicialización con dependencias: una entrada es de interés para el usuario de la clase ( tamaño ), la otra es una dependencia interna que solo sirve para confundir al usuario y es una implementación detalle ( widget ).
El parámetro de tamaño NO es una dependencia, es simple un valor de inicialización por instancia. IoC es excelente para las dependencias externas (como el widget) pero no para la inicialización del estado interno.
Peor aún, ¿qué pasa si el Widget solo es necesario para 2 de los 4 métodos en esta clase; ¡Puedo estar incurriendo en una sobrecarga de instanciación para Widget aunque no se pueda usar!
¿Cómo comprometer / conciliar esto?
Un enfoque es cambiar exclusivamente a interfaces para definir el contrato de operación; y abolir el uso de constructores por parte de los usuarios. Para ser coherente, todos los objetos tendrían que ser accedidos solo a través de interfaces, y instanciados solo a través de alguna forma de resolutor (como un contenedor IOC / DI). Solo el contenedor puede instanciar cosas.
Eso se encarga de la dependencia del widget, pero ¿cómo inicializamos el "tamaño" sin recurrir a un método de inicialización separado en la interfaz de Foo? Con esta solución, perdimos la capacidad de garantizar que una instancia de Foo esté completamente inicializada en el momento en que la obtenga. Qué fastidio, porque realmente me gusta la idea y la simplicidad de la inyección del constructor.
¿Cómo puedo lograr una inicialización garantizada en este mundo DI, cuando la inicialización es MÁS que SOLO dependencias externas?
fuente
La encapsulación pura es un ideal que nunca se puede lograr. Si todas las dependencias estuvieran ocultas, entonces no tendría la necesidad de DI en absoluto. Piénselo de esta manera, si realmente tiene valores privados que se pueden internalizar dentro del objeto, por ejemplo, el valor entero de la velocidad de un objeto de automóvil, entonces no tiene dependencia externa y no necesita invertir o inyectar esa dependencia. Este tipo de valores de estado interno que son operados exclusivamente por funciones privadas son lo que desea encapsular siempre.
Pero si está construyendo un automóvil que quiere un cierto tipo de objeto del motor, entonces tiene una dependencia externa. Puede crear una instancia de ese motor, por ejemplo, nuevo GMOverHeadCamEngine (), internamente dentro del constructor del objeto del automóvil, preservando la encapsulación pero creando un acoplamiento mucho más insidioso a una clase concreta GMOverHeadCamEngine, o puede inyectarlo, permitiendo que el objeto del automóvil funcione agnósticamente (y mucho más robusto) en, por ejemplo, una interfaz IEngine sin la dependencia concreta. Ya sea que use un contenedor de COI o una DI simple para lograr esto, no es el punto: el punto es que tiene un automóvil que puede usar muchos tipos de motores sin estar acoplado a ninguno de ellos, lo que hace que su base de código sea más flexible y menos propenso a los efectos secundarios.
DI no es una violación de la encapsulación, es una forma de minimizar el acoplamiento cuando la encapsulación se rompe necesariamente como algo natural en casi todos los proyectos de OOP. Inyectar una dependencia en una interfaz externamente minimiza los efectos secundarios de acoplamiento y permite que sus clases se mantengan agnósticas sobre la implementación.
fuente
Depende de si la dependencia es realmente un detalle de implementación o algo que el cliente desearía / necesitaría saber de una forma u otra. Una cosa que es relevante es a qué nivel de abstracción apunta la clase. Aquí hay unos ejemplos:
Si tiene un método que utiliza el almacenamiento en caché debajo del capó para acelerar las llamadas, entonces el objeto de caché debe ser Singleton o algo así y no debe inyectarse. El hecho de que el caché se esté utilizando en absoluto es un detalle de implementación que los clientes de su clase no deberían tener que preocuparse.
Si su clase necesita generar flujos de datos, probablemente tenga sentido inyectar el flujo de salida para que la clase pueda generar fácilmente los resultados en una matriz, un archivo o donde sea que otra persona quiera enviar los datos.
Para un área gris, digamos que tiene una clase que hace una simulación de Monte Carlo. Necesita una fuente de aleatoriedad. Por un lado, el hecho de que necesita esto es un detalle de implementación, ya que al cliente realmente no le importa exactamente de dónde proviene la aleatoriedad. Por otro lado, dado que los generadores de números aleatorios del mundo real hacen compensaciones entre el grado de aleatoriedad, velocidad, etc. que el cliente puede querer controlar, y el cliente puede querer controlar la siembra para obtener un comportamiento repetible, la inyección puede tener sentido. En este caso, sugeriría ofrecer una forma de crear la clase sin especificar un generador de números aleatorios, y usar un Singleton de subproceso local como predeterminado. Si surge la necesidad de un control más preciso, proporcione otro constructor que permita inyectar una fuente de aleatoriedad.
fuente
Creo en la simplicidad. La aplicación de la inyección de dependencia / IOC en las clases de dominio no mejora, excepto hacer que el código sea mucho más difícil de mantener al tener archivos xml externos que describen la relación. Muchas tecnologías como EJB 1.0 / 2.0 y struts 1.1 están retrocediendo al reducir las cosas que se ponen en XML e intentar ponerlas en código como anotación, etc. Por lo tanto, aplicar IOC para todas las clases que desarrolle hará que el código no tenga sentido.
IOC tiene beneficios cuando el objeto dependiente no está listo para la creación en tiempo de compilación. Esto puede suceder en la mayoría de los componentes de arquitectura de nivel abstracto de infraestructura, tratando de establecer un marco base común que deba funcionar para diferentes escenarios. En esos lugares, el uso de COI tiene más sentido. Aún así, esto no hace que el código sea más simple / mantenible.
Como todas las otras tecnologías, esto también tiene PRO y CON. Mi preocupación es que implementamos las últimas tecnologías en todos los lugares, independientemente de su mejor uso de contexto.
fuente
La encapsulación solo se rompe si una clase tiene la responsabilidad de crear el objeto (que requiere el conocimiento de los detalles de implementación) y luego usa la clase (que no requiere el conocimiento de estos detalles). Explicaré por qué, pero primero una anaología rápida del automóvil:
Pero volvamos a la codificación. La encapsulación es "ocultar un detalle de implementación de algo que usa esa implementación". La encapsulación es algo bueno porque los detalles de implementación pueden cambiar sin que el usuario de la clase lo sepa.
Cuando se usa la inyección de dependencia, la inyección del constructor se usa para construir objetos de tipo de servicio (a diferencia de los objetos de entidad / valor que modelan el estado). Cualquier variable miembro en el objeto de tipo de servicio representa detalles de implementación que no deben filtrarse. por ejemplo, número de puerto de socket, credenciales de la base de datos, otra clase a la que llamar para realizar el cifrado, un caché, etc.
El constructor es relevante cuando la clase se está creando inicialmente. Esto sucede durante la fase de construcción mientras su contenedor DI (o fábrica) conecta todos los objetos de servicio. El contenedor DI solo conoce los detalles de implementación. Sabe todo acerca de los detalles de implementación como lo saben los muchachos de la fábrica de Kombi sobre las bujías.
En tiempo de ejecución, el objeto de servicio que se creó se llama apon para hacer un trabajo real. En este momento, el llamador del objeto no sabe nada de los detalles de implementación.
Ahora, de vuelta a la encapsulación. Si los detalles de implementación cambian, entonces la clase que usa esa implementación en tiempo de ejecución no necesita cambiar. La encapsulación no está rota.
Si los detalles de implementación cambian, el contenedor DI (o fábrica) necesita cambiar. En primer lugar, nunca intentó ocultar los detalles de implementación de la fábrica.
fuente
Después de haber luchado un poco más con el problema, ahora considero que la inyección de dependencia (en este momento) viola la encapsulación en algún grado. Sin embargo, no me malinterpretes: creo que usar la inyección de dependencia vale la pena en la mayoría de los casos.
El caso de por qué DI viola la encapsulación se vuelve claro cuando el componente en el que está trabajando debe entregarse a una parte "externa" (piense en escribir una biblioteca para un cliente).
Cuando mi componente requiere que se inyecten subcomponentes a través del constructor (o propiedades públicas) no hay garantía para
Al mismo tiempo, no se puede decir que
Ambas citas son de wikipedia .
Para dar un ejemplo específico: necesito entregar una DLL del lado del cliente que simplifique y oculte la comunicación a un servicio WCF (esencialmente una fachada remota). Debido a que depende de 3 clases de proxy WCF diferentes, si adopto el enfoque DI me veo obligado a exponerlas a través del constructor. Con eso expongo los elementos internos de mi capa de comunicación que estoy tratando de ocultar.
Generalmente estoy a favor de DI. En este ejemplo particular (extremo), me parece peligroso.
fuente
DI viola la Encapsulación para objetos NO Compartidos - punto. Los objetos compartidos tienen una vida útil fuera del objeto que se está creando y, por lo tanto, deben AGREGARSE al objeto que se está creando. Los objetos que son privados para el objeto que se está creando deben COMPONERSE en el objeto creado: cuando el objeto creado se destruye, se lleva el objeto compuesto. Tomemos el cuerpo humano como ejemplo. Lo que está compuesto y lo que está agregado. Si tuviéramos que usar DI, el constructor del cuerpo humano tendría cientos de objetos. Muchos de los órganos, por ejemplo, son (potencialmente) reemplazables. Pero, todavía están compuestos en el cuerpo. Las células sanguíneas se crean en el cuerpo (y se destruyen) todos los días, sin la necesidad de influencias externas (que no sean proteínas). Por lo tanto, las células sanguíneas son creadas internamente por el cuerpo: nuevo BloodCell ().
Los defensores de DI argumentan que un objeto NUNCA debe usar el nuevo operador. Ese enfoque "purista" no solo viola la encapsulación sino también el Principio de sustitución de Liskov para quien está creando el objeto.
fuente
Luché con esta noción también. Al principio, el 'requisito' de usar el contenedor DI (como Spring) para crear una instancia de un objeto se sentía como saltar a través de aros. Pero en realidad, no es realmente un aro, es solo otra forma 'publicada' de crear objetos que necesito. Claro, la encapsulación está 'rota' porque alguien 'fuera de la clase' sabe lo que necesita, pero realmente no es el resto del sistema el que lo sabe: es el contenedor DI. Nada mágico sucede de manera diferente porque DI 'sabe' que un objeto necesita otro.
De hecho, se pone aún mejor: al centrarme en las fábricas y repositorios, ¡ni siquiera tengo que saber que la DI está involucrada en absoluto! Eso para mí pone la tapa de nuevo en la encapsulación. ¡Uf!
fuente
PD. Al proporcionar la inyección de dependencias que no necesariamente se rompe la encapsulación . Ejemplo:
El código del cliente aún no conoce los detalles de implementación.
fuente
Tal vez esta sea una forma ingenua de pensarlo, pero ¿cuál es la diferencia entre un constructor que toma un parámetro entero y un constructor que toma un servicio como parámetro? ¿Significa esto que definir un número entero fuera del nuevo objeto y alimentarlo en el objeto rompe la encapsulación? Si el servicio solo se usa dentro del nuevo objeto, no veo cómo eso rompería la encapsulación.
Además, al usar algún tipo de función de cableado automático (Autofac para C #, por ejemplo), hace que el código sea extremadamente limpio. Al construir métodos de extensión para el constructor Autofac, pude cortar MUCHO código de configuración DI que habría tenido que mantener con el tiempo a medida que crecía la lista de dependencias.
fuente
Creo que es evidente que al menos la DI debilita significativamente la encapsulación. Además de eso, aquí hay algunos otros inconvenientes de la DI a tener en cuenta.
Hace que el código sea más difícil de reutilizar. Un módulo que un cliente puede usar sin tener que proporcionar explícitamente dependencias, obviamente es más fácil de usar que uno donde el cliente tiene que descubrir de alguna manera cuáles son las dependencias de ese componente y luego ponerlas a disposición de alguna manera. Por ejemplo, un componente creado originalmente para usarse en una aplicación ASP puede esperar que sus dependencias sean proporcionadas por un contenedor DI que proporciona instancias de objetos con vidas relacionadas con las solicitudes http del cliente. Es posible que esto no sea sencillo de reproducir en otro cliente que no viene con el mismo contenedor DI incorporado que la aplicación ASP original.
Puede hacer que el código sea más frágil. Las dependencias proporcionadas por la especificación de la interfaz se pueden implementar de formas inesperadas, lo que da lugar a toda una clase de errores de tiempo de ejecución que no son posibles con una dependencia concreta resuelta estáticamente.
Puede hacer que el código sea menos flexible en el sentido de que puede terminar con menos opciones sobre cómo desea que funcione. No todas las clases necesitan tener todas sus dependencias en existencia durante toda la vida útil de la instancia propietaria, sin embargo, con muchas implementaciones de DI no tiene otra opción.
Con eso en mente, creo que la pregunta más importante se convierte en " ¿una dependencia particular necesita ser especificada externamente? ". En la práctica, rara vez he encontrado necesario hacer una dependencia externamente suministrada solo para soportar las pruebas.
Cuando una dependencia realmente necesita ser suministrada externamente, eso normalmente sugiere que la relación entre los objetos es una colaboración en lugar de una dependencia interna, en cuyo caso el objetivo apropiado es la encapsulación de cada clase, en lugar de la encapsulación de una clase dentro de la otra .
En mi experiencia, el principal problema con respecto al uso de DI es que si comienzas con un marco de aplicación con DI incorporado, o si agregas soporte de DI a tu base de código, por alguna razón la gente asume que, dado que tienes soporte de DI, ese debe ser el correcto manera de instanciar todo . Simplemente nunca se molestan en hacer la pregunta "¿esta dependencia debe especificarse externamente?". Y lo que es peor, también comienzan a tratar de obligar a todos los demás a usar el soporte DI todo .
El resultado de esto es que inexorablemente su base de código comienza a convertirse en un estado en el que crear cualquier instancia de cualquier cosa en su base de código requiere resmas de configuración obtusa del contenedor DI, y depurar cualquier cosa es el doble de difícil porque tiene la carga de trabajo adicional de tratar de identificar cómo y donde algo fue instanciado.
Entonces mi respuesta a la pregunta es esta. Use DI donde puede identificar un problema real que resuelve para usted, que no puede resolver más simplemente de otra manera.
fuente
Estoy de acuerdo en que llevado al extremo, DI puede violar la encapsulación. Por lo general, DI expone dependencias que nunca fueron realmente encapsuladas. Aquí hay un ejemplo simplificado tomado de Singletons de Miško Hevery que son mentirosos patológicos. :
Comienza con una prueba de CreditCard y escribe una prueba unitaria simple.
El próximo mes recibirá una factura por $ 100. ¿Por qué te acusaron? La prueba unitaria afectó a una base de datos de producción. Internamente, CreditCard llama
Database.getInstance()
. Refactorizar CreditCard para que tome unDatabaseInterface
en su constructor expone el hecho de que hay dependencia. Pero yo diría que, para empezar, la dependencia nunca se encapsuló, ya que la clase CreditCard causa efectos secundarios visibles desde el exterior. Si desea probar CreditCard sin refactorizar, ciertamente puede observar la dependencia.No creo que valga la pena preocuparse si exponer la base de datos como una dependencia reduce la encapsulación, porque es un buen diseño. No todas las decisiones de DI serán tan sencillas. Sin embargo, ninguna de las otras respuestas muestra un contraejemplo.
fuente
CreditCard
cambiar el objeto de un WebAPI a PayPal, sin que tenga que cambiar nada externo a la clase?Creo que es una cuestión de alcance. Cuando define la encapsulación (sin dejar saber cómo) debe definir cuál es la funcionalidad encapsulada.
Clasifique como está : lo que está encapsulando es la única responsabilidad de la clase. Lo que sabe hacer. Por ejemplo, ordenando. Si inyecta algún comparador para ordenar, digamos, clientes, eso no es parte de lo encapsulado: quicksort.
Funcionalidad configurada : si desea proporcionar una funcionalidad lista para usar, no está proporcionando la clase QuickSort, sino una instancia de la clase QuickSort configurada con un Comparador. En ese caso, el código responsable de crear y configurar eso debe estar oculto del código de usuario. Y esa es la encapsulación.
Cuando está programando clases, es decir, implementando responsabilidades individuales en clases, está utilizando la opción 1.
Cuando está programando aplicaciones, es decir, hacer algo que emprenda un trabajo concreto útil, entonces está utilizando repetidamente la opción 2.
Esta es la implementación de la instancia configurada:
Así es como lo usa algún otro código de cliente:
Está encapsulado porque si cambia la implementación (cambia la
clientSorter
definición del bean) no interrumpe el uso del cliente. Tal vez, a medida que usa archivos xml con todos escritos juntos, está viendo todos los detalles. Pero créame, el código del cliente (ClientService
) no sabe nada sobre su clasificador.fuente
Probablemente valga la pena mencionar que
Encapsulation
depende un poco de la perspectiva.Desde la perspectiva de alguien que trabaja en la
A
clase, en el segundo ejemploA
sabe mucho menos sobre la naturaleza dethis.b
Mientras que sin DI
vs
La persona que mira este código sabe más sobre la naturaleza del
A
segundo ejemplo.Con DI, al menos todo ese conocimiento filtrado se encuentra en un solo lugar.
fuente