我有一个基本控制器类,它使用autofac中的聚合服务来注入其依赖项。在从基本控制器派生的类中,我需要访问作为聚合容器的一部分的服务。哪一种更好?
public Interface ICommonControllerDependencies
{
ICookieService CookieService{ get; }
IAuthenticationService AuthenticationService{ get; }
}
public abstract class BaseController : Controller
{
protected readonly ICommonControllerDependencies Dependencies;
protected BaseController(ICommonControllerDependencies dependencies)
{
this.Dependencies = dependencies;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData[UserName] = Dependencies.AuthenticationService.UserName;
base.OnActionExecuting(filterContext);
}
protected ActionResult RedirectToDefaultPage()
{
var page = Dependencies.CookieService.GetDefaultPageCookie(Request, RouteData);
//Do Something
}
}
public class BaseReportController : BaseController
{
public BaseReportController(ICommonControllerDependencies dependencies)
: base(dependencies)
{
_cookieService = dependencies.CookieService;
}
}
OR
public class BaseReportController : BaseController
{
public BaseReportController(
ICookieService cookieService,
ICommonControllerDependencies dependencies)
: base(dependencies)
{
_cookieService = cookieService;
}
}
答案 0 :(得分:2)
AggregateService
旨在将您的子类与超类中的更改隔离开来,并在不破坏子类的情况下启用超类中的更改。
如果你选择选项1,你将子类耦合到你的超类,从而失去了预期的隔离。
在不了解您的设计的情况下,我会选择选项2.我认为我不能期望ServiceA
永远在那里。明确依赖于子类中的IServiceA
,该类可以快乐地忽略超类内部的变化。
答案 1 :(得分:1)
当您需要聚合服务时(如Autofac文档所述),这可能是违反Single Responsibility Principle的标志,它表明类应该做一件事,而且只做一件事。
换句话说,聚合服务模式是code smell *。
还有big base classes is a code smell,因为基类往往会增长并增长到大而复杂的类,它包含许多不是所有子类型都使用的代码。相反,一般建议是赞成composition over inheritance。
然而,对于ASP.NET MVC等UI框架,这并不总是容易的,因为大多数框架本身都会促进继承。
尝试提取基类的逻辑以分离依赖关系,尤其是当所有子类型不使用基类中的代码时。例如,您可以将RedirectToDefaultPage
方法重构为以下内容:
public class DefaultPageRedirector
{
private readonly ICookieService cookieService;
public DefaultPageRedirector(ICookieService cookieService)
{
this.cookieService = cookieService;
}
public ActionResult RedirectToDefaultPage(
Controller controller)
{
var page = this.cookieService.GetDefaultPageCookie(
controller.Request, controller.RouteData);
//Do Something
}
}
通过这种方式,您只能将DefaultPageRedirector
注入实际需要的Controller
类型。
对于OnActionExecuting
它是不同的,因为它被调用每个子类型。但是,ViewData["UserName"]
属性可能不会被系统中的每个View
使用,在这种情况下,您应该考虑将UserName
作为(静态类型){{1}的一部分返回对象。如果大多数视图使用它,您可能需要考虑使用局部视图,因为您的视图中可能有一些重复的代码(DRY原则不仅适用于代码,而且适用于系统中的每个部分)
这可能会消除基类中的大多数代码,这可能也会删除基类中的大部分依赖项(如果不是全部的话)。
*请注意,代码气味并不意味着总是出现问题。引用维基百科:“代码气味是程序源代码中的任何症状,可能表示更深层次的问题。”
答案 2 :(得分:0)
我认为这取决于您是否在容器中注册了IserviceA,以及您如何预测您的设计在不断发展。
就个人而言,如果您不希望从ICommonControllerDependencies接口中删除serviceA,我会使用第一个选项。注入单个接口似乎更简单。如果您认为可能会在某个时候删除serviceA,我会考虑使用第二个选项,因为它会删除BaseReportController中服务和控制器依赖关系之间的耦合。我认为第二种选择更紧密地遵循Law of Demeter。