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

发布于 2024-11-18 00:02:23 字数 554 浏览 5 评论 0原文

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

当调用 Load() 方法时,它会通过 GetAll() 检索订单详细信息,例如

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

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

如您所见,OrderCustomer 类的依赖项,但是,我不能只创建一个 IOrder 并将其注入其中接口不能有静态方法。

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

我不想将 GetAll() 设为实例方法,因为它是静态方法并且需要保持这种状态。

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

Imagine there is a Customer class with an instance Load() method.

When the Load() method is called, it retrieves order details by e.g.

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

GetAll() is a static method of the Order class and the input parameters are fields defined in the Customer class.

As you can see, Order is a dependency of the Customer class, however, I can't just create an IOrder and inject it there as interfaces can't have static methods.

Therefore, the question is how could I introduce dependency injection in this example?

I don't want to make GetAll() an instance method since it's a static method and need to keep it that way.

For example, I have used utility classes in my design, most of which just contain static methods.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

送舟行 2024-11-25 00:02:23

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

像这样:

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

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

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

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

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.)

浅笑依然 2024-11-25 00:02:23

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

这是一个例子:

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

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();
    }
}
多情癖 2024-11-25 00:02:23

函数指针注入

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

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

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 的答案),可能会更容易。但如果你真的想将其保留为静态函数,那么这个解决方案将起作用。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文