想象一下,Customer
类有一个实例Load()
方法。
当调用Load()
方法时,它会通过例如
var orders = Order.GetAll(customerId, ...);
GetAll()
是Order
类的静态方法,输入参数是Customer
类中定义的字段。
正如您所看到的,Order
是Customer
类的依赖项,但是,我不能只创建一个IOrder
并将其注入,因为接口不能具有静态方法
因此,问题是如何在此示例中引入依赖注入?
我不想让GetAll()
成为一个实例方法,因为它是一个静态方法,需要保持这种方式。
例如,我在我的设计中使用了实用程序类,其中大多数只包含静态方法。
答案 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的答案),则可能会更容易。但是如果你真的想把它作为一个静态函数,那么这个解决方案就可以了。