在2010年的一次.NET伦敦小组会议上,简短地讨论了一个话题,这让深思熟虑。在自己的项目中开始实践,现在,有足够的信心来分享想法。以下是观点,如果同意或不同意,请在评论中告诉。
一定听说过:“控制器不应该臃肿,控制器应该包含最少的代码”,这是一条准则。在SOLID原则中,S代表单一职责原则(SRP)。控制器应该只有一个角色,它是模型和视图之间的通信层。即使在这个角色中,它也违反了SRP,有人可能会说,也许需要两个类来承担控制器的角色,或者每个动作方法一个控制器,但先不深入讨论这个问题。
控制器的角色本身就承担了过多的责任,再给它增加更多的责任就是设计上的罪过(也许应该因为这样做而失去一些StackOverflow或CodeProject的积分)。
为什么控制器会变胖?之前写过一篇博客,讨论了视图如何变得臃肿:,其中一些观点是通用的,一些是特定于控制器的:
在动作方法中显式验证数据 在控制器中验证视图模型数据违反了两个原则:单一职责原则(SRP)和封装的面向对象原则(OOP)。ASP.NET MVC 4提供了一个很好的验证生态系统,可以帮助将验证代码移到它应该在的地方。
映射模型到视图模型 模型到视图模型的映射不应该在动作方法中进行。最喜欢的解决方案是使用CQRS类型的查询模式,这样就不需要在任何地方进行这种样板代码。这里有一个关于CQRS的很好的。
泄露一些业务逻辑 这里不是放置任何业务逻辑的地方。控制器应该调用服务,或者是经典领域驱动设计(DDD)的实现,或者是发出CQRS的查询或命令。
动作方法中的HTTP相关代码 需要特殊处理的特殊HTTP案例不应该在这里完成。动作过滤器可能是答案。
在单元测试什么? 鉴于上述情况,如果动作方法几乎没有代码,那么在单元测试什么?想测试是否返回了正确的视图,还是想测试是否用正确的属性装饰了动作方法?不会这样做,通常有一个截止日期要达到,反对样板代码。
如果有人告诉他们的控制器需要单元测试,会问,设计控制器的方式正确吗?
结论:单元测试是提供更高质量软件的手段,而不是目的本身。有太多的冗余单元测试会导致未来的维护问题,并且会增加项目开发时间。
// 示例代码,展示如何使用CQRS模式
public class QueryHandler : IQueryHandler
{
private readonly IRepository _repository;
public QueryHandler(IRepository repository)
{
_repository = repository;
}
public ProductViewModel Handle(GetProductQuery query)
{
var product = _repository.Get(query.ProductId);
return product == null ? null : new ProductViewModel
{
ProductId = product.ProductId,
Name = product.Name,
Price = product.Price
};
}
}