2010-03-25 6 views
11

Estamos construyendo un proyecto ASP.NET y encapsulando toda nuestra lógica comercial en clases de servicio. Algunos están en los objetos de dominio, pero en general son bastante anémicos (debido al ORM que estamos usando, eso no cambiará). Para habilitar mejor las pruebas unitarias, definimos interfaces para cada servicio y utilizamos D.I.E. Aquí hay un par de las interfaces:¿Por qué no agrupar todas las clases de servicio en un método de fábrica (en lugar de inyectar interfaces)?

IEmployeeService 
IDepartmentService 
IOrderService 
... 

Todos los métodos de estos servicios son básicamente grupos de tareas, y las clases no contienen variables miembro privadas (excepto las referencias a los servicios dependientes). Antes de preocuparnos por Unit Testing, simplemente declararíamos todas estas clases como estáticas y las llamaríamos directamente. Ahora vamos a configurar la clase de este tipo si el servicio depende de otros servicios:

public EmployeeService : IEmployeeService 
{ 
    private readonly IOrderService _orderSvc; 
    private readonly IDepartmentService _deptSvc; 
    private readonly IEmployeeRepository _empRep; 

    public EmployeeService(IOrderService orderSvc 
         , IDepartmentService deptSvc 
         , IEmployeeRepository empRep) 
    { 
     _orderSvc = orderSvc; 
     _deptSvc = deptSvc; 
     _empRep = empRep; 
    } 
    //methods down here 
} 

Esto realmente no suele ser un problema, pero me pregunto por qué no creó una clase de fábrica que se pasa alrededor de su lugar?

decir

public ServiceFactory 
{ 
    virtual IEmployeeService GetEmployeeService(); 
    virtual IDepartmentService GetDepartmentService(); 
    virtual IOrderService GetOrderService(); 
} 

Entonces, en lugar de llamar a:

_orderSvc.CalcOrderTotal(orderId) 

que llamaríamos

_svcFactory.GetOrderService.CalcOrderTotal(orderid) 

¿Cuál es la caída de este método? Todavía es comprobable, todavía nos permite usar D.I. (y manejar dependencias externas como contextos de bases de datos y remitentes de correo electrónico a través de D.I. dentro y fuera de la fábrica), y elimina una gran cantidad de D.I. configurar y consolida dependencias más.

Gracias por su opinión!

Respuesta

2

Si la mayoría de sus clases dependen de estas tres interfaces, puede pasar un objeto que las envuelva, PERO: si la mayoría de las clases solo depende de una o dos de ellas, no es una buena idea ya que esas clases tienen acceso a objetos que no necesitan y con los que no tienen ningún negocio y algunos programadores siempre llaman al código que se supone que no deben llamar solo porque está disponible.

Por cierto, no es una fábrica a menos que siempre cree un objeto nuevo en los métodos [...] Get Service() y hacerlo solo por pasar algunos métodos es malo. Simplemente lo llamaría ServiceWrapper y los convertiría en las propiedades EmployeeService, DepartmentService y OrderService.

+0

"No es una buena idea, ya que esas clases tendrán acceso a objetos que no necesitan y no tienen nada que ver con y algunos programadores siempre llaman al código que se supone que no deben llamar solo porque está disponible ". - Buen punto, marcando esto como la respuesta. – Andrew

4

Un argumento en contra de esto es que no deja claras sus dependencias. Muestra que usted depende de "algunas de las cosas en la fábrica de servicios", pero no de qué servicios. Para propósitos de refactorización, puede ser útil saber exactamente qué depende de qué.

inyección de dependencias debe hacer este tipo de cosas fácil, si está utilizando un marco apropiado - que sólo debería ser una cuestión de crear el constructor de la derecha, la definición de lo cual implementa la interfaz, y dejar que ordenar todo.

+0

Sí, estamos usando StructureMap, que * típicamente * maneja todo bien. Sin embargo, esto se basa en la comprobación de errores de tiempo de ejecución, y si alguien accidentalmente olvidó marcar una clase como pública, no lo sabemos hasta probar el programa. También hay problemas cuando los servicios dependen el uno del otro: S/M obtiene excepciones de stackoverflow y tenemos que resolver las cosas de forma manual en lugar de la resolución de autodependencia. Un método de fábrica parece eliminar todos estos problemas. – Andrew

+0

1) Con StructureMap, puede validar su contenedor en una prueba de unidad o al inicio de la aplicación: container.AssertConfigurationIsValid() 2) Si obtiene excepciones de desbordamiento de pila, o tiene una cadena de dependencia circular (mala) o se ha tropezado sobre un error que desconocemos (indíquenos github.com/structuremap) 3) El último código de mapa de estructura (de github) tiene un ensamblaje StructureMap.AutoFactory que probablemente hace lo que usted desea. Consulte la discusión en http://groups.google.com/group/structuremap-users/browse_thread/thread/d1003ce9038403f5 –

3

Dicha fábrica es esencialmente un Servicio de Localización de, y I consider it an anti-pattern porque oscurece sus dependencias y hacen que sea muy fácil de violar la Single Responsibility Principle (SRP).

Uno de los muchos beneficios excelentes que derivamos de Constructor Injection es que it makes violations of the SRP so glaringly obvious.

+0

Estoy de acuerdo, pero es probable que haya muchas dependencias inyectadas que sean BUENAS. Considere una aplicación que estamos escribiendo ahora que maneja el cálculo de un cheque de pago. El cálculo de un cheque es algo que sucede en un solo lugar. Utiliza rutinas comunes de muchos servicios diferentes, pero los 36 pasos requeridos para crear el cheque de pago (calcular impuestos, deducciones, beneficios, etc.) no se usan en ninguna otra parte. No importa cómo lo dividas, todavía hay 36 pasos para crear el cheque de pago. Al mantenerlo en un lugar, es muy obvio cómo se calcula un cheque. – Andrew

+0

El hecho de que solo lo use en un lugar no significa que no pueda dividir el cálculo en varias clases más pequeñas, cada una con sus propias dependencias, ocultas detrás de Fachadas y Servicios agregados. Personalmente, nunca me gustaría mantener una sola clase con 36 pasos. Incluso el analizador de mantenimiento muy tolerante de Visual Studio muy probablemente se quejará de una sola clase que realice estos pasos. Tal cálculo suena más fácil de mantener como un Subdominio completo que como un Transaction Srcipt. –

+0

Pero si se trata de un proceso por lotes con 36 pasos, ¿no está ocultando el proceso escondiéndolo detrás de otros servicios? Tomar un script de transcripción y dividirlo por el simple hecho de romperlo parece un poco tonto, pero tal vez sea solo yo. – Andrew

Cuestiones relacionadas