如何将抽象工厂注入实体的方法?

时间:2014-07-30 17:54:51

标签: unit-testing dependency-injection mocking domain-driven-design inversion-of-control

我有一个Order实体,其refund()方法使用抽象工厂refundStrategyFactory在运行时创建适当的退款策略:

public class Order extends Entity{

    public void refund(Employee emp, Quantity qty, IRefundStrategyFactory refundStartegyFactory){
        //use the abstract factory to determine the appropriate strategy at run-time.
    }
}

我正在尝试使用依赖注入并将IRefundStrategyFactory自动注入refund()方法,但我找不到实现此目的的方法。

我没有使用构造函数注入,因为Order是一个实体,我们不应该将服务注入实体,除非它将像退款方法一样暂时使用。 Misko Hevery的博文To “new” or not to “new”…更好地解释了这一点:

  

Newable可以了解Injectable。什么是不好的   Newable有一个对Injectable的字段引用。

answer Mark Seemann对相关问题的类似问题也提出了同样的想法。

奖励点,我是否通过公开其对refund()的依赖来揭露IRefundStrategyFactory方法的内部结构?我应该单独牺牲单元测试并在方法本身内创建工厂吗?

3 个答案:

答案 0 :(得分:1)

理想情况下,您的域模型应该是免费的基础架构问题,IOC是基础架构问题,建议域模型是简单的POJO。所以我不会将bean注入我的域模型。

在这种情况下,我认为您应该有一个应用服务,它将注入工厂类,然后将工厂类作为参数传递给Order类。

工厂类是否使用Order类中的一些信息来决定实例化哪种策略类?

  

奖励点,Aren我公开了refund()方法的内部   暴露其对IRefundStrategyFactory的依赖?

我不确定是否存在暴露方法内部的事情,这个概念更自然地适用于"暴露类的内部",一个方法做了一些动作并且这样做它可能需要来自外部世界的一些信息,我们可以讨论如何传递这些信息以及这种方法是否做得太多了? ......但我无法推断一种方法是否会暴露其实施",也许你应该详细说明你的意思

答案 1 :(得分:1)

我通过启动最终一致性的退款流程来解决此问题,您可以在其中触发Refund操作。然后,此操作的听众及其活动将执行自己的任务,即退还商品的成本,重新放置商品等。或者,您可以在触发多个域服务的应用程序服务中以原子方式执行此操作,一个接一个地传递Order项目:

FinancialService.RefundOrderGoods(myOrder);
StockService.RestockOrderItems(myOrder);
...

我避免将任何服务或存储库添加到您的实体中。

答案 2 :(得分:1)

你的主要问题是你不能“自动注入到refund()方法”,但IMO根本不是问题,因为你可以简单地将依赖关系传递给方法,但仍然使用构造函数注入在调用此方法的服务中:

public class ApplyRefundHandler : ICommandHandler<ApplyRefund>
{
    private readonly IRefundStrategyFactory refundStartegyFactory;
    private readonly IRepository<Order> orderRepository;
    private readonly IRepository<Employee> employeeRepository;

    public ApplyRefundHandler(IRefundStrategyFactory refundStartegyFactory,
        IRepository<Order> orderRepository,
        IRepository<Employee> employeeRepository)
    {
        this.refundStartegyFactory = refundStartegyFactory;
        this.orderRepository = orderRepository;
        this.employeeRepository = employeeRepository;
    }

    public void Handle(ApplyRefund command)
    {
        Order order = this.orderRepository.GetById(command.OrderId);
        Employee employee = this.employeeRepository.GetById(command.EmployeeId);
        order.refund(employee, command.Quantity, this.refundStartegyFactory);
    }
}
  

我是不是通过公开它来暴露退款()方法的内部   对IRefundStrategyFactory的依赖?我应该牺牲单元测试吗?   孤立并在方法本身内创建工厂?

是的,但同时您可能通过在Order课程中添加大量业务逻辑来违反单一责任原则。如果隐藏抽象,则意味着您需要将它们注入构造函数中,您可能会很快发现Order类获得了很多依赖项。因此,您可以将Order的方法视为单一职责(伪装的一个类),与该类中的其他方法几乎没有任何关系,在这种情况下,将其依赖项传递给该方法是有意义的。

作为旁边节点,请注意工厂抽象,例如IRefundStrategyFactory通常不是很好的抽象。您应该考虑将其删除,详见here