How to use Dependency Injection with Static Methods?

女生的网名这么多〃 提交于 2019-11-28 21:32:27

If you must keep the static method, I would wrap the static calls in a Repository object.

Like this:

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

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

Now you inject this repository into your Customer class.

(I'm assuming you're doing this so you can inject fake IOrders at runtime for testing purposes. I should say that in general, static methods are a serious obstacle to testing.)

Mike

Seeing as your aggregate root for fetching orders is your customer model I would strongly advise you create a customer repository and inject that to whatever service requires it.

Here is an example:

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();
    }
}

Function Pointer Injection

TLDR:

Inject a function pointer into the Customer class. The value of this function pointer can be Order.GetAll in production, and MockOrder.GetAll in tests.

EXAMPLE:

The dependency (problematic static function we depend on):

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

Our dependent class (depends on static function):

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

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

Production client class (performs the dependency injection):

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

Testing client class (how unit test can inject a fake dependency):

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
    }
}

DISCUSSION:

How you actually inject the "function pointer" will depend on the syntax and features available in your language. Here I'm just talking about the general concept.

This isn't exactly a pretty solution. It would probably be easier if you can change GetAll to be an instance method (perhaps by introducing an OrdersLoader object, or by using Paul Phillips' answer). But if you really want to keep it as a static function, then this solution will work.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!