我已经将问题最小化了:我有一个返回客户剩余限制的类。我只是在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}}?
问题:我很困惑。参数化实例化是否必要?我可以在没有它的情况下测试我的代码,因为我可以模拟客户,资金和限制服务。我只是在控制器上使用这个类,参数化实例化在这里是否夸大了?
简单地说:我的第一个没有参数化实例化的代码是反模式吗?可以,还是应该使用参数化实例化或自定义工厂?
我自己找不到这些问题的满意答案。这就是我需要你帮助的原因。
感谢您的帮助!
答案 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
是没有意义的。如果控制器在生活中的目的是协调这三种服务的连接 - ICustomerService
,IMoneyService
和ILimitService
- 那么让它接受它需要的依赖关系并调用它一天。
然而,我猜测根据我自己的经验,控制器真的不是你想要的地方,所以如果是我......我会选择小提供者界面。
奖励:在这种情况下,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));
}
}