我正在查看我网站上的控制器,他们的大多数构造函数都是这样的:
public SomeController(
IServiceOne serviceOne,
IServiceTwo serviceTwo,
ILoggingService loggingService,
IGeospatialService geoSpatialService)
{
// copy to class variables.
}
换句话说,它非常多毛,使重构变得困难。一些控制器有大约8个依赖项。
有什么方法可以将这些依赖关系“分组”到其中一个桶中?
例如,每个控制器都需要ILoggingService
,执行空间内容的控制器需要IGeospatialService
,而在某些情况下只需要IServiceOne
和IServiceTwo
。
我希望看到类似的内容:
public SomeController(
ICoreServicesGroup coreGroup,
ISomeNameForServicesGroup serviceGroup)
{
// copy to class variables.
}
我认为引入一些OO技术会很好,例如有一个“基础”依赖类,它在其受保护的ctor中需要ILoggingService
。然后你可能有另一个继承的子依赖等等。
以前有人这样做过吗?这是StructureMap可以为我做的事情,还是只是我自己编写基本代码?
答案 0 :(得分:13)
<强>登录强>
当每个控制器都需要依赖关系时,它是一个非常确定的指标,它不是“正常”依赖关系,而是交叉关注关注。记录是跨领域关注的典型例子,因此ILoggingService
应该像任何其他跨领域关注一样处理。
在SOLID OO中,解决跨领域问题的适当方法是使用Decorator(can be generalized towards AOP)。但是,ASP.NET MVC Controller操作方法不是任何接口的一部分,因此这是一个不太理想的解决方案。
相反,MVC框架提供Action Filters用于拦截目的。如果你想实现一个松散耦合的过滤器,请自己帮个忙implement it as a global filter instead of an attribute。
其他依赖
对于其他依赖项,它对refactor them to Facade Services有意义。这涉及识别相关服务的自然集群,因此具体如何完成此操作是针对每个代码库的。
答案 1 :(得分:3)
我知道我不久前接受了@Mark Seeman的回答,但我现在终于有时间实现了这个,所以我想分享我实际做的事情,为了其他人的利益。
基本上,我在应用程序中为依赖项的“组”创建了包装器接口。
示例:
public interface ICoreServicesDependencyGroup
{
IUnitOfWork UnitOfWork { get; }
IAspNetMvcLoggingService LoggingService { get; }
}
实施:
public class CoreServicesDependencyGroup : ICoreServicesDependencyGroup
{
private readonly IAspNetMvcLoggingService _loggingService;
private readonly IUnitOfWork _unitOfWork;
public CoreServicesDependencyGroup(
IAspNetMvcLoggingService loggingService,
IUnitOfWork unitOfWork)
{
Condition.Requires(loggingService).IsNotNull();
Condition.Requires(unitOfWork).IsNotNull();
_loggingService = loggingService;
_unitOfWork = unitOfWork;
}
public IUnitOfWork UnitOfWork { get { return _unitOfWork; } }
public IAspNetMvcLoggingService LoggingService { get { return _loggingService; } }
}
非常简单。
然后我更新了我的控制器。
示例ctor之前:
public LocationController(
IUnitOfWork unitOfWork,
IAspNetMvcLoggingService loggingService,
ILocationService locationService,
ICachedLocationService cachedLocationService)
{
_unitOfWork = unitOfWork;
_loggingService = loggingService;
_locationService = locationService;
_cachedLocationService = cachedLocationService;
}
后:
public LocationController(
ICoreServicesDependencyGroup coreServicesDependencyGroup,
ILocationDependencyGroup locationDependencyGroup)
{
_unitOfWork = coreServicesDependencyGroup.UnitOfWork;
_loggingService = coreServicesDependencyGroup.LoggingService;
_locationService = locationDependencyGroup.Service;
_cachedLocationService = locationDependencyGroup.CachedService;
}
真的没什么特别的,只是套装。在引擎盖下,控制器仍然使用相同的依赖关系,但是ctor签名更小,更易读,而且还使单元测试更容易。
答案 2 :(得分:0)
我看到了几个选项:
public class Logger
{
public static Func<ILoggerService> Instance = () => ServiceLocator.Current.GetInstance<ILoggerService>();
}
public class Logger
{
public static Func<ILoggerService> Instance = () => ServiceLocator.Current.GetInstance<ILoggerService>();
}
用法:
测试:Logger.Instance().Log(message);