2011-06-29 10 views
8

Imagine que hay una clase Customer con un método de instancia Load().Cómo utilizar la inyección de dependencia con métodos estáticos?

Cuando se llama al método Load(), recupera los detalles del pedido, p. Ej.

var orders = Order.GetAll(customerId, ...); 

GetAll() es un método estático de la clase Order y los parámetros de entrada son campos definidos en la clase Customer.

Como puede ver, Order es una dependencia de la clase Customer, sin embargo, no puedo simplemente crear un IOrder e insertarlo allí ya que las interfaces no pueden tener métodos estáticos.

Por lo tanto, la pregunta es ¿cómo podría introducir la inyección de dependencia en este ejemplo?

No quiero hacer GetAll() un método de instancia, ya que es un método estático y debe mantenerlo así.

Por ejemplo, he utilizado clases de utilidad en mi diseño, la mayoría de las cuales solo contienen métodos estáticos.

Respuesta

9

Si debe mantener el método estático, envolvería las llamadas estáticas en un objeto Repositorio.

De esta manera:

interface IOrderRepository { 
    IEnumerable<IOrder> GetAll(customerId, ..); 
} 

class OrderRepository : IOrderRepository { 
    IEnumerable<IOrder> GetAll(customerId, ...) 
    { 
    Order.GetAll(customerId,...); // The original static call. 
    } 
} 

Ahora se inyecte este repositorio en su clase Customer.

(estoy asumiendo que usted está haciendo esto para que pueda inyectar IOrders falsos en tiempo de ejecución para realizar pruebas. Debo decir que, en general, los métodos estáticos son un serio obstáculo para la prueba.)

+0

Gracias, estas clases de repositorio y las interfaces serán parte de la capa de acceso a datos? Mi clase de pedido es una entidad comercial mapeada de la tabla de pedidos (utilizando LINQ to SQL). –

+0

@ user133212 es solo una envoltura alrededor de su función existente. entonces, donde sea que esa función lógicamente pertenezca a capas, también pondría el contenedor. –

3

Al ver como su agregar raíz para obtener órdenes es su modelo de cliente Me gustaría fuertemente aconsejarle que cree un repositorio de clientes e inyectarlo a cualquier servicio que lo requiera.

Aquí se muestra un ejemplo:

public class CustomerService 
{ 
    private readonly ICustomerRepository _customerRepository; 

    public CustomerService(ICustomerRepository customerRepository) 
    { 
     if (customerRepository == null) 
     { 
      throw new ArgumentNullException("customerRepository"); 
     } 

     _customerRepository = customerRepository; 
    } 

    public IEnumerable<IOrder> GetOrdersForCustomerId(int customerId) 
    { 
     return _customerRepository.GetOrdersForCustomerId(customerId); 
    } 
} 

public interface ICustomerRepository 
{ 
    IEnumerable<IOrder> GetOrdersForCustomerId(int customerId); 
} 

class CustomerRepository : ICustomerRepository 
{ 
    public IEnumerable<IOrder> GetOrdersForCustomerId(int customerId) 
    { 
     throw new NotImplementedException(); 
    } 
} 
0

función de puntero inyección

TLDR:

inyectar un puntero de función en la clase Customer. El valor de este puntero de función puede ser Order.GetAll en producción y MockOrder.GetAll en pruebas.

Ejemplo:

La dependencia (problemática función estática dependemos de):

class Order { 
    static func GetAll() -> [Order] { 
     var orders = ... // Load from production source 
     return orders 
    } 
} 

Nuestra clase dependiente (depende de la función estática):

class Customer { 
    func Init(getAllOrdersFunction) { // Arg is a func pointer 
     self.getAllOrdersFunction = getAllOrdersFunction 
    } 

    func Load() { 
     var orders = self.getAllOrdersFunction() 
     // Do stuff... 
    } 
} 

Producción clase de cliente (realiza la dependencia injecti en):

class BusinessLogicManager { 
    func DoBusinessLogic() { 
     var customer = Customer(Order.GetAll) // Prod func injected here 
     customer.Load() 
     // Do stuff... 
    } 
} 

clase de cliente Testing (qué prueba de la unidad puede inyectar una dependencia falsa):

class CustomerUnitTests { 
    static func GetFakeOrders() { 
     var orders = ... // Hardcoded test data 
     return orders 
    } 

    func TestLoad() { 
     var customer = Customer(CustomerUnitTests.GetFakeOrders) // Fake func injected here 
     customer.Load() 
     // Verify results given known behavior of GetFakeOrders 
    } 
} 

DISCUSIÓN:

cómo en realidad inyectar el "puntero de función" se Depende de la sintaxis y las funciones disponibles en su idioma. Aquí estoy hablando del concepto general.

Esto no es exactamente una solución bonita. Probablemente sería más fácil si puede cambiar GetAll para que sea un método de instancia (quizás introduciendo un objeto OrdersLoader, o usando la respuesta de Paul Phillips). Pero si realmente desea mantenerlo como una función estática, entonces esta solución funcionará.

Cuestiones relacionadas