¿Debería volver a la forma antigua? Mi respuesta en resumen es no. DI tiene numerosos beneficios por todas las razones que mencionaste.
menudo me siento como que estoy creando las interfaces para el motivo de interfaces
Si usted está haciendo esto que podría estar violando la Reused Abstractions Principle (RAP)
Dependiendo de los requisitos de servicio, constructores algunas clases puede obtener muy grande, lo que hará que la clase sea completamente inútil en otros contextos donde y si no se utiliza un IoC.
Si sus clases constructores son demasiado grandes y complejas, esta es la mejor manera de mostrar que usted está violando una muy importante otro principio: Single Reponsibility Principle. En este caso es el momento de extraer y refactorizar su código en diferentes clases, el número de dependencias sugeridas es alrededor de 4.
Para hacer DI no es necesario que tenga una interfaz, DI es la forma en que consigue tus dependencias en tu objeto. Crear interfaces puede ser una forma necesaria para poder sustituir una dependencia con fines de prueba. A menos que el objeto de la dependencia es:
- Fácil aislar
- no habla a los subsistemas externos (sistema de archivos etc)
puede crear su dependencia como una clase abstracta, o cualquier clase donde los métodos que desea sustituir son virtuales. Sin embargo, las interfaces crean la mejor forma desconectada de una dependencia.
En algunos casos, p. al crear instancias de Entidades nuevas en tiempo de ejecución, uno necesita acceder al contenedor/ker de IoC para crear la instancia. Esto crea una dependencia en el propio contenedor de IoC (ObjectFactory en SM, una instancia del kernel en Ninject), que realmente va con el motivo de usar uno en primer lugar. ¿Cómo puede ser esto resuelto? Las fábricas abstractas vienen a la mente, pero eso solo más complica el código.
En cuanto a la dependencia del contenedor IOC, nunca debería tener una dependencia en las clases de cliente. Y no es necesario.
Para utilizar por primera vez la inyección de dependencia es necesario comprender el concepto de Composition Root. Este es el único lugar donde se debe hacer referencia a su contenedor. En este punto, se construye todo el gráfico de objetos. Una vez que comprenda esto, se dará cuenta de que nunca necesita el contenedor en sus clientes. Como cada cliente acaba de obtener su dependencia inyectada.
También hay muchos otros patrones de creación que puede seguir para hacer la construcción más fácil: decir que quiere construir un objeto con muchas dependencias como este:
new SomeBusinessObject(
new SomethingChangedNotificationService(new EmailErrorHandler()),
new EmailErrorHandler(),
new MyDao(new EmailErrorHandler()));
puede crear una fábrica de cemento que sabe cómo construir este:
public static class SomeBusinessObjectFactory
{
public static SomeBusinessObject Create()
{
return new SomeBusinessObject(
new SomethingChangedNotificationService(new EmailErrorHandler()),
new EmailErrorHandler(),
new MyDao(new EmailErrorHandler()));
}
}
y luego usarlo como esto:
SomeBusinessObject bo = SomeBusinessObjectFactory.Create();
También puede utilizar pobre sirve di y crear un constructor que no toma ningún argumento en todo:
public SomeBusinessObject()
{
var errorHandler = new EmailErrorHandler();
var dao = new MyDao(errorHandler);
var notificationService = new SomethingChangedNotificationService(errorHandler);
Initialize(notificationService, errorHandler, dao);
}
protected void Initialize(
INotificationService notifcationService,
IErrorHandler errorHandler,
MyDao dao)
{
this._NotificationService = notifcationService;
this._ErrorHandler = errorHandler;
this._Dao = dao;
}
Entonces sólo parece que se utiliza para trabajar:
SomeBusinessObject bo = new SomeBusinessObject();
Usando DI hombre pobre se considera mal cuando las implementaciones predeterminadas se encuentran en bibliotecas externas de terceros, pero menos graves cuando se tiene una buena implementación predeterminada.
Luego, obviamente, están todos los contenedores DI, constructores de Objetos y otros patrones.
Así que todo lo que necesita es pensar en un buen patrón de creación para su objeto. Su objeto en sí no debería importar cómo crear las dependencias, de hecho las hace MÁS complicadas y hace que mezclen 2 tipos de lógica. Por lo tanto, no creo que el uso de DI deba tener pérdida de productividad.
Existen algunos casos especiales en los que su objeto no puede obtener una sola instancia inyectada. Donde la duración es generalmente más corta y se requieren instancias sobre la marcha. En este caso debe inyectarse la fábrica en el objeto como una dependencia:
public interface IDataAccessFactory
{
TDao Create<TDao>();
}
Como se puede notar esta versión es genérico, ya que puede hacer uso de un contenedor IoC para crear diversos tipos (Tome en cuenta sin embargo el contenedor IoC todavía no es visible para mi cliente).
public class ConcreteDataAccessFactory : IDataAccessFactory
{
private readonly IocContainer _Container;
public ConcreteDataAccessFactory(IocContainer container)
{
this._Container = container;
}
public TDao Create<TDao>()
{
return (TDao)Activator.CreateInstance(typeof(TDao),
this._Container.Resolve<Dependency1>(),
this._Container.Resolve<Dependency2>())
}
}
Aviso Solía activador pesar de que tenía un contenedor COI, esto es importante tener en cuenta que la fábrica necesita para construir una nueva instancia de objeto y no sólo asumir el envase proporcionará una nueva instancia que el objeto puede estar registrado con diferentes tiempos de vida (Singleton, ThreadLocal, etc.). Sin embargo, dependiendo del contenedor que esté utilizando, puede generar estas fábricas para usted. Sin embargo, si está seguro de que el objeto está registrado con una vida útil transitoria, simplemente puede resolverlo.
EDIT: Adición de clase con la dependencia Abstract Factory:
public class SomeOtherBusinessObject
{
private IDataAccessFactory _DataAccessFactory;
public SomeOtherBusinessObject(
IDataAccessFactory dataAccessFactory,
INotificationService notifcationService,
IErrorHandler errorHandler)
{
this._DataAccessFactory = dataAccessFactory;
}
public void DoSomething()
{
for (int i = 0; i < 10; i++)
{
using (var dao = this._DataAccessFactory.Create<MyDao>())
{
// work with dao
// Console.WriteLine(
// "Working with dao: " + dao.GetHashCode().ToString());
}
}
}
}
Básicamente haciendo DI/COI ralentiza drásticamente mi productividad y en algunos casos complica aún más el código y la arquitectura
Marcos Seeman escribió un blog impresionante sobre el tema, y respondió la pregunta: Mi primera reacción a ese tipo de pregunta es: usted dice que el código débilmente acoplado es más difícil de entender. Más difícil que qué?
Loose Coupling and the Big Picture
EDIT: Por último, me gustaría señalar que no todos los objetos y de dependencia o necesidades deben ser inyectada dependencia, en primer lugar considerar si lo que está utilizando es actualmente considerada como una dependencia:
Lo son dependencias?
- configuración de la aplicación
- los recursos del sistema (reloj)
Bibliotecas
- Terceros
- base de datos
- WCF/Red de Servicios
- sistemas externos (Archivo/Correo electrónico)
Cualquier de los objetos o colaboradores anteriores pueden estar fuera de su control y causar efectos secundarios fectos y diferencias en el comportamiento y lo hacen difícil de probar. Estos son los momentos para considerar una Abstracción (Clase/Interfaz) y usar DI.
¿Qué no son dependencias, realmente no necesita DI?
List<T>
- MemoryStream
- Cuerdas/Primitives
- objetos hoja/del Dto
objetos como el anterior, simplemente se pueden crear instancias donde sea necesario el uso de la palabra clave new
. No recomendaría usar DI para objetos tan simples a menos que haya razones específicas. Considere la pregunta si el objeto está bajo su control total y no causa ningún gráfico adicional de objetos o efectos secundarios en el comportamiento (al menos cualquier cosa que quiera cambiar/controlar el comportamiento o la prueba). En este caso, simplemente recógelos.
He publicado muchos enlaces a las publicaciones de Mark Seeman, pero realmente le recomiendo que lea sus publicaciones de libros y blogs.
Excelente respuesta, directamente sobre el tema, ¡gracias! Leí los dos artículos que vinculó, que son una gran fuente de información y en realidad respondieron muchas otras preguntas que tenía en mente. La violación de RAP y SRP se destacan como cosas buenas para tener en cuenta en mi caso. Una última cosa que se destaca de su respuesta, menciona el uso de fábricas abstractas para ocultar un contenedor IoC de su cliente, ¿está bien entonces que una fábrica abstracta dependa de un contenedor IoC? – edvaldig
Gracias, me alegro de que esto te haya ayudado, y yo diría que sí, que la fábrica abstracta es muy aceptable para saber sobre el contenedor, ya que la fábrica opera en la misma capa que la raíz de composición en el 'pegamento' y no contiene lógica de negocios, simplemente gobierna las instancias concretas. Por lo tanto, en un entorno de prueba es posible que ni siquiera use un contenedor Ioc y necesitaría una fábrica diferente porque el tipo concreto sería diferente – Andre
Hola, acabo de agregar otra edición al final para que comprenda qué es una dependencia, y no preocuparse por cualquier gestión de dependencia para esos objetos que son simples y no lo requieren. – Andre