我有3个服务组件,一个负责某种数据序列化的低级服务,一个负责协调保存/加载,一个负责API发布的MVC控制器。
3个组成部分中的每一个逻辑上都指向另一个“下方”。中间服务具有另一个参数,该参数在运行时基于请求数据而已知。从这3个组件中,控制器和中间服务由类表示(引入接口没有意义,因为没有任何模拟),最低级别由接口重复,使其可用于单元测试中间服务或控制器。我想使用DI(特别是Ninject)来构建我的控制器类。我的问题是,是否存在任何处理此方案的最佳实践。目前我看到两种实施方式。 (为了清楚起见,省略了验证,正确实施。)
首先,这是中间服务和低级接口的示例实现。
public interface ISerializer {
void Serialize(object data);
}
public class MyService {
private string _dataId;
private ISerializer _serializer;
public MyService(string dataId, ISerializer serializer) {
_serializer = serializer;
_dataId = dataId;
}
public bool CanProcess(MyDTO data) {
...
}
public void DoSomeProcessing(MyDTO data) {
...
}
}
public class MyController : Controller {
private Func<string, MyService> _myServiceFactory;
public MyController(Func<string, MyService> myServiceFactory) {
_myServiceFactory = myServiceFactory;
}
...
[HttpPost]
public JsonResult Process(string dataId, MyDTO model) {
using (var myService = _myServiceFactory(dataId)) {
...
if (myService.CanProcess(model))
myService.DoSomeProcessing(model);
...
return Json("ok");
}
}
}
public class MyController : Controller {
private ISerializer _serializer;
public MyController(ISerializer serializer) {
_serializer = serializer;
}
...
[HttpPost]
public JsonResult Process(string dataId, MyDTO model) {
using (var myService = new MyService(dataId, _serializer) {
...
if (myService.CanProcess(model))
myService.DoSomeProcessing(model);
...
return Json("ok");
}
}
}
哪一个更合适,或者我应该选择一个完全不同的解决方案?
答案 0 :(得分:1)
首先,我喜欢我的服务是无国籍的,所以我不喜欢传递dataId
的想法
到服务的构造函数。当服务无国籍时,它们更安全。如果他们当前处于有效状态,您可以调用他们的方法而不用担心。它还使测试和模拟它们变得更容易。您还可以减少已用内存量,因为您只需要一个无状态服务实例。
如果您将dataId
移动到DoSomeProcessing
作为参数,则可以使用Ninject轻松实例化MyService,并自动注入正确的ISerializer实现。
但是,如果你坚持将它传递给构造函数“Version 1”,那就非常接近我认为好的了。当构造函数中还需要数据参数时,工厂是让DI注入依赖项的好方法。我会将MyServiceFactory
注入控制器。我会为它创建另一个类:
public class MyServiceFactory : IMyServiceFactory // an interface to me able to mock it if needed
{
ISerializer _serializer;
MyServiceFactory(ISerializer serializer){ // here Ninject can inject the dependency
_serializer = serializer;
}
IMyService Create(int dataId){ // here you can pass additional parameter
return new MyService(dataId, _serializer);
}
}
通过这种方式,您可以轻松避免硬依赖性,并使代码更易于维护和更易于测试。
“版本2”错了。如果你想测试你的控制器或用另一个实现替换MyService - 你就会陷入困境。您将不得不进行大量繁琐的重构(取决于使用量)。最后,你最终会得到与我上面建议类似的东西。 :)