如何使用静态方法的依赖注入?

时间:2011-06-29 15:48:22

标签: dependency-injection static-methods

想象一下,Customer类有一个实例Load()方法。

当调用Load()方法时,它会通过例如

检索订单详细信息
var orders = Order.GetAll(customerId, ...);

GetAll()Order类的静态方法,输入参数是Customer类中定义的字段。

正如您所看到的,OrderCustomer类的依赖项,但是,我不能只创建一个IOrder并将其注入,因为接口不能具有静态方法

因此,问题是如何在此示例中引入依赖注入?

我不想让GetAll()成为一个实例方法,因为它是一个静态方法,需要保持这种方式。

例如,我在我的设计中使用了实用程序类,其中大多数只包含静态方法。

3 个答案:

答案 0 :(得分:12)

如果必须保留静态方法,我会将静态调用包装在Repository对象中。

像这样:

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

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

现在您将此存储库注入Customer类。

(我假设你这样做,所以你可以在运行时为测试目的注入假IOrders。我应该说,一般来说,静态方法是测试的严重障碍。)

答案 1 :(得分:3)

看到您获取订单的聚合根是您的客户模型,我强烈建议您创建一个客户存储库并将其注入任何需要它的服务。

以下是一个例子:

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

答案 2 :(得分:1)

功能指针注入

TLDR:

将函数指针注入Customer类。此函数指针的值在生产中可以是Order.GetAll,在测试中可以是MockOrder.GetAll

实施例

依赖关系(我们依赖的有问题的静态函数):

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

我们的依赖类(依赖于静态函数):

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

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

生产客户端类(执行依赖注入):

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

测试客户端类(单元测试如何注入假依赖):

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

讨论:

实际注入“函数指针”的方式取决于您的语言中提供的语法和功能。在这里,我只是谈论一般概念。

这不是一个非常漂亮的解决方案。如果您可以将GetAll更改为实例方法(可能通过引入OrdersLoader对象,或使用Paul Phillips的答案),则可能会更容易。但是如果你真的想把它作为一个静态函数,那么这个解决方案就可以了。