why services net iservicecollection inyeccion injection example dependency dependencias configureservices asp c# .net design-patterns dependency-injection ioc-container

c# - iservicecollection - services asp net core



IoC Factory: Pros y contras para Interface versus Delegates (5)

En cualquier lugar donde necesite un valor de tiempo de ejecución para construir una dependencia particular, Abstract Factory es la solución.

Mi pregunta es: ¿Por qué muchas fuentes prefieren FactoryInterface sobre FactoryDelegate para implementar este patrón? ¿Cuáles son los pros y los contras de ambas soluciones?

Aquí hay un ejemplo para entender lo que quiero decir.

Si tiene un Servicio que necesita un Repositorio con un Contexto determinado, entonces el constructor del Servicio necesita una fábrica para crear o acceder a su repositorio.

La solución común para esto es crear un RepositoryFactoryInterface como este.

public IRepositoryFactory { IRepository Create(ContextInformation context); } public class MyService { private IRepositoryFactory repositoryFactory; public MyService(IRepositoryFactory repositoryFactory) { this.repositoryFactory = repositoryFactory: } public void DoSomeService() { ContextInformation context = ....; IRepository repository = this.repositoryFactory.Create(context); repository.Load(...); ... repository.Save(...); } }

También es necesario implementar la interfaz IRepositoryFactory de alguna manera

public MyEf4RepositoryFactory : IRepositoryFactory { IRepository Create(ContextInformation context) { return new MyEf4Repository(context); } }

... y usarlo en la aplicación

public void main() { IRepositoryFactory repoFactory = new MyEf4RepositoryFactory(); IService service = new MyService(repoFactory); service.DoSomeService(); }

----- Fin de la solución general ------

En lugar de RepositoryFactoryInterface, puedes hacer lo mismo con un factorydelegate que requiere menos codificación como esta.

public class MyService { private Func<ContextInformation, IRepository> repositoryFactory; public MyService(Func<ContextInformation, IRepository> repositoryFactory) { this.repositoryFactory = repositoryFactory: } public void DoSomeService() { ContextInformation context = ....; IRepository repository = this.repositoryFactory(context); repository.Load(...); ... repository.Save(...); } }

... y usarlo en la aplicación

public void main() { IService service = new MyService(context => new MyEf4Repository(context)); service.DoSomeService(); }

En mi opinión, factorydelegate context => new MyEf4Repository(context) es mucho más compacto que declarar e implementar una interfaz IRepositoryFactory y MyEf4RepositoryFactory .

Debe haber una razón para esto y quiero saber por qué.

Aquí hay una fuente de ejemplo que utiliza el enfoque de la interfaz: responder a es-hay-un-patrón-para-inicializar-objetos-creados-a través de un di-container

[Actualización] 15 meses después de hacer esta pregunta y tener más experiencia con los universers de Java, cambié de opinión: ahora prefiero las interfaces a los delegados. Pero no puedo decir por qué. Es solo un sentimiento. Tal vez porque estoy más acostumbrado a ello?


En cualquier lugar donde necesite un valor de tiempo de ejecución para construir una dependencia particular, Abstract Factory es la solución.

Yo argumentaría en contra de esto. Las dependencias no se deben construir utilizando datos de tiempo de ejecución, como se explica aquí . En resumen el artículo dice:

No inyecte datos de tiempo de ejecución en los componentes de la aplicación durante la construcción; causa ambigüedad, complica la raíz de la composición con una responsabilidad adicional y hace que sea extraordinariamente difícil verificar la exactitud de su configuración DI. En su lugar, deje que los datos de tiempo de ejecución fluyan a través de las llamadas de método de los gráficos de objetos construidos.

Cuando permitimos que los datos de tiempo de ejecución "fluyan a través de las llamadas de método de los gráficos de objetos construidos" en su lugar, verá que la utilidad de Abstract Factories disminuye. Pueden seguir utilizándose cuando los datos de tiempo de ejecución se utilizan para elegir entre múltiples dependencias (en comparación con la inyección de datos de tiempo de ejecución en una dependencia), pero incluso entonces las Fábricas abstractas no suelen ser la mejor solución, como se explica aquí . En resumen el artículo dice:

En general, el uso de una abstracción de fábrica no es un diseño que tenga en cuenta sus consumidores. De acuerdo con el Principio de Inyección de Dependencias (DIP), las abstracciones deben ser definidas por sus clientes, y como una fábrica aumenta el número de dependencias en las que se ve obligado a depender, la abstracción claramente no se crea a favor del cliente y, por lo tanto, podemos Considera que esto está en violación de la DIP.

En cambio, los patrones como Fachada, Compuesto, Mediador y Proxy son generalmente una mejor solución.

Eso no significa que no pueda tener código en su aplicación que genere dependencias, pero no debe definirse como una abstracción utilizada por otros componentes de la aplicación. En su lugar, el comportamiento similar a una fábrica debe encapsularse en adaptadores que se definen como parte de su Raíz de composición .

Cuando solo tiene esta lógica de fábrica y dependencias como parte de su Raíz de composición, no importa si define una IRepositoryFactory o simplemente usa una Func<IRepository> para construir dicha dependencia, ya que la IRepositoryFactory se definirá en el Composición de la raíz también (ya que la aplicación no tiene ningún negocio en el uso de dicha fábrica).

Dicho esto, en el raro caso de que una Abstract Factory sea la abstracción correcta (que normalmente ocurrirá cuando se está construyendo un marco reutilizable), encuentro que el uso de interfaces de fábrica es mucho más revelador que el uso de los delegados. Es un poco más detallado, pero mucho más claro cuál es el significado de tal cosa. Un IControllerFactory es más revelador que Func<IController> .

Yo diría que esto es aún más válido para las fábricas que no producen dependencias sino valores de datos. Tomemos, por ejemplo, el ejemplo de inyectar un Func<DateTime> en un constructor. ¿Qué significa esto realmente y qué valor devuelve? ¿Es intuitivo que devuelva un DateTime.Now , o devuelve DateTime.Today , o algo más? En ese caso, sería mucho más claro definir una interfaz ITimeProvider con un método GetCurrentTime() .

NOTA: Esta respuesta se actualizó en julio de 2017 para reflejar mis últimas opiniones.


Creo que llamar a una "fábrica" ​​un delegado no debería ser correcto.

Factory tiene la responsabilidad de crear instancias de algún tipo compatible, como la implementación de un repositorio.

Hacer con delegados no tiene sentido porque está definiendo cómo se crea una instancia de su repositorio cada vez que desea crear una instancia de su servicio.

La pregunta para mí es ¿por qué debería usar un delegado si puede implementar un parámetro genérico TRepository con restricciones "nuevas" y "de clase" y, en tiempo de construcción, crear una instancia del repositorio dentro de su servicio?

Es solo una opinión, pero parece que desea un atajo en lugar de una solución mejor, bien diseñada y óptima.

Resumiendo:

  • La fábrica sobre un delegado permite la inversión de control incluso para la propia fábrica.

  • El delegado sobre la fábrica tiene una ventaja nula, ya que incluso puede eliminar la necesidad de un delegado en sí mismo, tal vez, entonces, sea inútil o redundante .

  • Delegar "fábrica" ​​no será una fábrica porque se implementarán N formas de crear una instancia de repositorio, rompiendo la necesidad y la ventaja de la fábrica.

  • No es necesario crear manualmente instancias de algún repositorio en el código del consumidor. Simplemente use un parámetro genérico para que se pueda proporcionar un tipo de repositorio para crear una instancia apropiada, gracias a la inversión de control.


El objetivo de utilizar la fábrica abstracta en una fábrica simple es agrupar fábricas individuales.

En su caso, si alguna vez necesitó que su delegado produjera algo diferente a un IRepository , estaría en problemas.


Me gusta y uso la solución de delegado ya que es más concisa. Para evitar el problema de legibilidad con los contenedores IoC que mencionó el Sr. Happy, no utilice Func. En su lugar crea tu propio delegado nombrado.

delegate IRepository RepositoryFactory(ContextInformation context);

Ahora tiene lo mejor de ambos mundos: la concisión de los delegados y la legibilidad de los contenedores IoC.


Personalmente, siempre he usado la solución principal, simplemente porque no pensé en usar un delegado.

Después de pensarlo, enfrenté el problema de la separación de preocupaciones. Estoy usando Ninject, y no quería que mi módulo de enlace tuviera este aspecto (imagina que el repositorio tiene algunas dependencias de sí mismo):

class IoCModule : NinjectModule { public override Load() { Bind<Func<Context, IRepository>>() .ToConstant( context => new MyEf4Repository(context, Kernel.Get<IRepositoryDependency1>, Kernel.Get<IRepositoryDependency2>) ); } }

Eso no es legible en absoluto. Así que todavía utilizaba fábricas abstractas completamente escritas para la Separación de la Preocupación y la legibilidad.

Ahora uso el FuncModule descrito en this pregunta (a la AutoFac). Así que puedo hacer esto:

class IoCModule : NinjectModule { public override Load() { Bind<IRepository>().To<MyEf4Repository>(); Bind<IRepositoryDependency1>().To<...>(); Bind<IRepositoryDependency2>().To<...>(); } }

y deja que Ninject resuelva las dependencias para mí. Como puede ver, es más legible que usar el método descrito anteriormente y tener que vincular las fábricas para cada dependencia. Esta es la forma en que hice la transición de la solución principal a la solución de delegado.

Así que para responder a su pregunta. La razón por la que utilicé la solución principal fue porque al principio no sabía cómo hacerlo de otra manera (esto se debe en parte a que la mayoría de los blogs escribían las fábricas abstractas, ¿puedes ver el círculo?).