Autofac参数化实例化(自定义工厂)vs新的Resolve类

时间:2016-03-15 19:00:41

标签: c# design-patterns dependency-injection autofac factory

我已经将问题最小化了:我有一个返回客户剩余限制的类。我只是在Mvc控制器(驱动程序项目)中初始化这个类,而不是在服务或其他项目中。

RemaningDeposit

有些控制器只需要RemaningWithdrawal,其中一些只需要 [Authorize] public class DepositController : Controller { private readonly IMoneyService _moneyService; private readonly ILimitService _limitService; private readonly ICustomerService _customerService; public DepositController(ICustomerService customerService, IMoneyService moneyService, ILimitService limitService) { _moneyService = moneyService; _limitService = limitService; _customerService = customerService; } public ActionResult GetDepositLimit() { var customer = _customerService.Find(User.Identity.Name); var accountLimit = new AccountLimit(customer.Id,_moneyService,_limitService); return View(accountLimit.RemainingDeposit); } } ,其中一些需要两者。 其中之一:

public class AccountLimit : IAccountLimit

然后我想我可以使用由Autofac自动生成的Parameterized Instantiation来将强类型参数传递给分辨率函数。

然后我的班级将有 [Authorize] public class DepositController : Controller { private readonly Func<long, IAccountLimit> _accountLimit; private readonly ICustomerService _customerService; public DepositController(ICustomerService customerService, Func<long, IAccountLimit> accountLimit) { _accountLimit = accountLimit; _customerService = customerService; } public ActionResult GetDepositLimit() { var customer = _customerService.Find(User.Identity.Name); var myAccountLimit = _accountLimit(customer.Id); return View(myAccountLimit.RemainingDeposit); } } 接口 我的存款控制器将是这样的:

new AccountLimit

没有参数化实例化我的代码中有Func<long, IAccountLimit> _accountLimit。我的控制器引用了IMoney和ILimitService。

使用参数化实例化我的代码中没有新内容我没有引用。但是,如果其他开发人员不使用Autofac,他们如何解决或启动此{{1}}?

问题:我很困惑。参数化实例化是否必要?我可以在没有它的情况下测试我的代码,因为我可以模拟客户,资金和限制服务。我只是在控制器上使用这个类,参数化实例化在这里是否夸大了?

简单地说:我的第一个没有参数化实例化的代码是反模式吗?可以,还是应该使用参数化实例化或自定义工厂?

我自己找不到这些问题的满意答案。这就是我需要你帮助的原因。

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

假设您的观点是简化控制器并使其更容易测试,而不是使用参数化实例化,我可能会使用工厂接口和支持它的简单实现。从关注点分离的角度来看,无论如何,我总觉得很多那种业务逻辑不应该在控制器中。

您从这个构造函数开始:

public DepositController(
  ICustomerService customerService,
  IMoneyService moneyService,
  ILimitService limitService)

然后重构(正确)似乎是为了消除控制器对IMoneyService的依赖性,因为控制器实际上不需要知道实例化AccountLimit个对象,毕竟,关键是要达到极限的一部分。 IMoneyService的唯一原因毕竟是AccountLimit

public DepositController(
  ICustomerService customerService,
  Func<long, IAccountLimit> accountLimit)

但是,从代码段开始,您可以使用ICustomerService执行相同的操作。如果你有这个怎么办?

public DepositController(Func<string, IAccountLimit> accountLimit)

然后你可以传递用户名并直接找到事物的内容。

var limit = _accountLimit(User.Identity.Name);

但是你提到你想要测试一些合理的东西,并且能够在构造函数中轻松地模拟东西,所以我想说一个简单的解决方案就是重构一个小的提供者接口。

public interface IUserLimitProvider
{
  IAccountLimit GetLimitForUser(string userName);
}

实现非常简单,直接来自原始控制器代码。

public class UserLimitProvider : IUserLimitProvider
{
  public UserLimitProvider(
    ICustomerService customerService,
    IMoneyService moneyService,
    ILimitService limitService)
  {
    // store these in private fields
  }

  public IAccountLimit GetLimitForUser(string userName)
  {
    var customer = _customerService.Find(userName);
    return new AccountLimit(customer.Id, _moneyService, _limitService);
  }
}

我猜你可能希望在那个工厂中出现比实现更强大的错误处理,所以在这样的单独方法中使用它而不是在Autofac注册中使用lambda将使测试更容易。

如果控制器的整个点是是那个提供者 ,那么首先重构构造函数的Func<long, IAccountLimit> accountLimit是没有意义的。如果控制器在生活中的目的是协调这三种服务的连接 - ICustomerServiceIMoneyServiceILimitService - 那么让它接受它需要的依赖关系并调用它一天。

然而,我猜测根据我自己的经验,控制器真的不是你想要的地方,所以如果是我......我会选择小提供者界面。

奖励:在这种情况下,ICustomerService的整个点似乎是将用户名映射到用户ID。我猜你在几个地方都这样做。您可能需要考虑使用ClaimsPrincipal并在用户登录时向主体添加权利主张。然后使用User.Identity.Name而不是使用ICustomerService您可以直接从主体获取用户的ID声明 - 跳过所有查找 - 并使用适当的ID直接进入服务。这是一个不错的性能增益,特别是如果该映射始终是数据库调用。此外,它还消除了在一些地方对V1_0__baseline.sql依赖关系的需求,简化了您在此类挑战中遇到的其他问题。

答案 1 :(得分:0)

我觉得你正以一种不必要的复杂方式解决这个问题。我在AccountLimit上看到的唯一依赖项是IMoneyService和ILimitService。

应将CustomerId传递到AccountLimit的每个“属性”中(使其方法接受 CustomerId 作为参数)。除了在2个相关服务之间进行编排之外,您的AccountLimit没有任何地方维护有关CustomerId的状态信息,因此不需要将其存储在其状态中。

我会像:

那样实现它
    public class AccountLimit
    {
        private readonly IMoneyService _moneyService;
        private readonly ILimitService _limitService;

        public AccountLimit(IMoneyService moneyService,ILimitService limitService)
        {
            _moneyService = moneyService;
            _limitService = limitService;
        }

        private decimal WithdrawalSum(long customerId) {  return _moneyService.GetTotalWithdrawal(customerId); }

        private decimal WithDrawalLimit(long customerId) {  return _moneyService.withDrawalLimit(customerId); }

        public decimal RemainingWithdrawal(long customerId) {  return withdrawalLimit(customerId) - withdrawalSum(customerId); } 
    }

并在您的控制器中:

    [Authorize]
    public class DepositController : Controller
    {
        private readonly IAccountLimit _accountLimit;
        private readonly ICustomerService _customerService;

        public DepositController(ICustomerService customerService, IAccountLimit accountLimit)
        {
            _accountLimit = accountLimit;
            _customerService = customerService;
        }

        public ActionResult GetWithdrawalLimit()
        {
            var customer = _customerService.Find(User.Identity.Name);

            return View(_accountLimit.RemainingWithdrawal(customer.Id));
        }
    }