utilizar tipos qué que patron manejo inyección inyeccion injection desventajas dependency dependencias cuales consideras casos apropiado c# dependency-injection interface namespaces dependencies

tipos - patron de inyeccion de dependencias c#



Espacio de nombres C#para interfaces en inyección de dependencia (1)

Quiero usar el patrón de Dependency Injection en C #, y quiero tener las lógicas lo más separadas posible en espacios de nombres.

Pregunta

¿En qué espacio de nombres debería estar la interface de la clase consumida?

Motivación de la pregunta

Primero hagamos un caso "normal". Una libreta de libros para usar como base para la segunda parte de la explicación. Luego, el caso de la "vida real", que plantea la pregunta.

Librero

Supongamos que el codificador es Alicia y que usa a Alice como un nombre de alto nivel en los espacios de nombres como proveedor, para evitar cualquier conflicto con otros codificadores. Para este ejemplo, asumiremos que no hay otras Alices en el mundo.

Supongamos que crea 3 espacios de nombres:

  • Alice.Invaders : un juego que proporcionará compras en la aplicación a través de una tienda.
  • Alice.Shop : una tienda reutilizable para varios juegos.
  • Alice.Injector : un administrador de servicios reutilizable.

Supongamos que el proyecto Shop tiene una interface llamada IShopService que proporciona un método Show() .

Supongamos que los Invaders tienen algún tipo de controlador que, en alguna acción del usuario, quiere abrir la tienda.

Supongamos que el controlador de Invaders obtiene los servicios, como la Shop , a través de un ServiceManager .

Alice.Injector

Alice.Injector es un proyecto independiente que no tiene dependencias, por lo que no usa la palabra clave "using":

namespace Alice.Injector { public interface IService { // All the services shall implement this interface. // This is necessary as C# is heavily typed and the // Get() method below must return a known type. } public class ServiceManager { static public IService Get( string serviceName ) { IService result; // Do the needed stuff here to get the service. // Alice implements this getter configurable in a text-file // so if she wants to test the invaders with a mock-shop // that does not do real-purchases but fake-ones // she can swap the injected services without changing the // consumer''s code. result = DoTheNeededStuff(); return result; } } }

Alice.Shop

Alice.Shop es también un proyecto independiente que (excepto en el caso de que consuma servicios) no tiene conocimiento de la existencia de un inyector. Solo es una tienda y eso es todo.

Como Alice piensa que quizás Bob vaya a comprar algo mejor algún día, ella prepara su clase para ser inyectada en dependencia, luego de la separación de la Shop en una interfaz de IShop y luego la implementación, siguiendo este artículo: https://msdn.microsoft. com / library / hh323705% 28v = vs.100% 29.aspx

Para lograrlo, Alice hará que la tienda sea un servicio compatible con el Alice.ServiceManager , por lo que Alice decide que la IShop se renombrará como IShopService y será una especie de IService de IService .

using Alice.Injector namespace Alice.Shop { public interface IShopService : IService { public void Show(); } public class Shop : IShopService { public void Show() { // Here Alice puts all the code to open the shop up. } } }

Alice.Invaders

Finalmente, Alice codifica el juego. El código de Alice.Invaders obtiene una Shop (que toma la forma de un servicio) a través del ServiceManager por lo que es todo código limpio.

using Alice.Injector using Alice.Shop namespace Alice.Invaders { public class DefaultController { void OnShopClick() { IShopService shop = ServiceManager.Get( "Shop" ) as IShopService; shop.Show(); } } }

Hasta aquí, todo esto funciona bien.

Caso de la vida real

Entonces ahora ... Bob (un buen amigo de Alice, como todos ustedes saben, curiosos de que no hablen sobre enviar mensajes encriptados hoy), hace una tienda súper agradable que es incluso más bonita que la que Alice hizo. Bob hace su tienda desde cero.

Entonces, Bob implementa la tienda compatible con el inyector de Alice (ya que Bob también usa Alice.Injector para inyectar otras cosas en sus proyectos).

using Alice.Injector namespace Bob.Shop { public interface IShopService : IService { public void Show(); } public class Shop : IShopService { public void Show() { // Here Bob does a brand new shop from scratch. } } }

Entonces ... ¡aquí está la situación extraña!

  • Bob.Shop Namespace para la interfaz - Si Bob hace su tienda dentro del espacio de nombres Bob.Shop la manera que se muestra arriba, entonces Alice debe editar su código para hacer referencia a Bob.Shop para obtener la interfaz IShopService (fea ella tiene que cambiar la dependencia en el código, ya que se suponía que debía usar Inyectores de Dependencia para deshacerse de cambiar las dependencias en el código).
  • Sin espacio de nombres para la interfaz : si tanto Alice como Bob configuran el IShopService en el espacio de nombres global, también es feo, ya que hay muchas cosas que podrían entrar en conflicto.
  • Espacio de nombres Alice.Shop para la interfaz : si Bob usa el sentido común y dice "Lo que quiero hacer es crear una tienda compatible con la de Alice, entonces debería implementar su interfaz HER", por lo que el código de Bob probablemente será como este :

Código de Bob utilizando Alice.Shop compatibilidad de espacio de nombres hacia atrás:

namespace Bob.Shop { public class Shop : Alice.Shop.IShopService { public void Show() { // Here Bob does a brand new shop from scratch, // which borrows Alice''s interface. } } }

En este caso, parece que todo está en su lugar:

  • Bob puede crear el Bob.Shop.Shop que implemente el Alice.Shop.IShopService
  • Alice no necesita cambiar una sola línea de código.
  • Alice.Injector.ServiceManager puede proporcionar otro IService al servir el Bob.Shop.Shop .

Problema

Todavía hay una dependencia aquí:

Alice.Invaders está lanzando el Alice.Injector.IService a un Alice.Shop.IShopService para poder llamar al método Show() . Si no haces ese reparto, no puedes "mostrar la tienda".

Entonces, al final, usted está "dependiendo" de ese elenco y, por lo tanto, "alguien" necesita proporcionarle la definición de la interfaz.

Si la tienda original no fue escrita por Alice, sino por Charlie, sería "feo" tener que descargar y conservar una copia del proyecto Charlie.Shop para poder utilizar Bob.Shop .

Asi que...

Preguntas

1) ¿Cuál es el espacio de nombres correcto para que IShopInterface viva?

2) ¿El proyecto de "reemplazo" proporciona su propia interfaz o toma prestada la original?

3) ¿Debería tal vez dividirse la tienda original en DOS proyectos? (como, por ejemplo, Alice.Shop y Alice.ShopImplementation para que Alice.Shop sea ​​muy delgada y solo contenga las interfaces? Tal vez Alice.Shop y Alice.Shop.Implementation como espacio de nombres anidado, pero aún dos bases de código separadas para que pueda descargar ¿Cómo instalar Alice.Shop sin descargar Alice.Shop.Implementation ?

4) ¿Es tan simple como que Bob incluye una copia del archivo Alice.Shop.IShopInterface en su proyecto por lo que no se necesitan dependencias? Muy feo: si lo hace y queremos tener las 2 tiendas y enviar a los usuarios a una u otra tienda, eso entraría en conflicto.

Gracias.


Las interfaces, el inyector y la implementación deben estar en diferentes espacios de nombres. Las interfaces deben estar en Alice.Shop.Interfaces y no debe haber implementaciones en este espacio de nombres. Puede cambiar / ocultar la implementación, pero debe seguir con Interfaces en Inyección de dependencia.

Alice.Invaders está lanzando el Alice.Injector.IService a un Alice.Shop.IShopService para poder llamar al método Show (). Si no haces ese reparto, no puedes "mostrar la tienda".

Su implementación de DefaultController no es buena. Si quiero usarlo, no sé nada sobre qué servicios necesito. Me dice, no necesito nada ahora.

Deberías usar la inyección de constructor.

public class DefaultController { private readonly IShopService _shopService; DefaultController(IShopService shopService) { _shopService=shopService; } void OnShopClick() { _shopService.Show(); } }

Si necesito el controlador predeterminado, sabría qué servicios necesito con esta implementación. Y no necesitas lanzar.

Editar:

Digamos que Alice tiene una tienda. Ella dice que quiero una sala de lectura que tenga 5 sillas. Pero ella decidirá que las sillas son de madera o cuero (IChairs). Cuando abre la tienda, decide usar sillas de madera (Inject WoodChairs para IChairs).

Entonces Bob compra la tienda de Alice. No puede cambiar la sala de lectura (es difícil tomará tiempo y la sala de lectura está bien). Pero él quiere sillas de cuero, así que usa sillas de cuero (Inject LeatherChairs para IChairs).

Bob debería Alice.Shop.Interfaces si no puede o no quiere cambiar la sala de lectura.

Pero digamos Me gusta Alice Reading Room mucho. Quiero diseñar una sala de lectura como la de ella. Pero quiero establecer mis reglas para la sala de lectura (adaptador IMyReadingRoom, se obtiene la clase ReadingRoom y no la interfaz y se crean sus propias interfaces).

En resumen : debe pegar interfaces siempre. Puede crear su propia interfaz ( adaptador ) para bibliotecas de terceros. De esta forma, puede extender u ocultar las reglas sin apegarse a la biblioteca de terceros (pero debe seguir con su propia interfaz de todos modos). Debe escribir el adaptador para la implementación de una biblioteca de terceros, no para sus interfaces.

Si Alice.Shop.Interfaces opción del adaptador, Bob tiene que usar Alice.Shop.Interfaces para inyectar.