使用服务定位而不是构造函数注入来避免编写工厂类的负载是否不好
现在我们使用 DI/IOC,当我们需要将额外的参数传递给构造函数时,我们使用工厂类,例如
public class EmailSender
{
internal EmailSender(string toEmail, string subject,String body, ILogger emailLogger)
{.....}
}
public class EmailSenderFactory
{
ILogger emailLogger;
public EmailSenderFactory(ILogger emailLogger)
{
this.emailLogger = emailLogger;
}
public EmailSender Create(string toEmail, string subject, string body)
{
return new EmailSender(toEmail, subject, body, emailLogger);
}
}
现在的问题是我们最终创建了很多工厂类,而人们并不总是知道如何使用他们(有时他们自己更新它们)。像这样编写类的最大缺点是什么:
public class EmailSender
{
EmailLogger logger = IoC.Resolve<ILogger>();
internal EmailSender(string toEmail, string subject,String body)
{.....}
}
优点:我们现在可以安全地使用构造函数,而不需要工厂类 缺点:我们必须引用服务定位器(我不担心可测试性,它很容易使用模拟容器作为容器的支持服务)。
难道有什么大的理由让我们不应该这样做吗?
编辑:经过一番思考,我发现通过拥有一个私有构造函数并嵌套 Factory 类,我可以将实现和工厂保持在一起,并防止人们不正确地创建类,所以问题变得有点没有实际意义。所有关于 SL 很脏的观点当然都是正确的,所以下面的解决方案让我很高兴:
public class EmailSender
{
public class Factory
{
ILogger emailLogger;
public Factory(ILogger emailLogger)
{
this.emailLogger = emailLogger;
}
public EmailSender Create(string toEmail, string subject, string body)
{
return new EmailSender(toEmail, subject, body, emailLogger);
}
}
private EmailSender(string toEmail, string subject,String body, ILogger emailLogger)
{
}
}
Right now we use DI/IOC and when we need to pass extra parameters to a constructor we use a factory class e.g.
public class EmailSender
{
internal EmailSender(string toEmail, string subject,String body, ILogger emailLogger)
{.....}
}
public class EmailSenderFactory
{
ILogger emailLogger;
public EmailSenderFactory(ILogger emailLogger)
{
this.emailLogger = emailLogger;
}
public EmailSender Create(string toEmail, string subject, string body)
{
return new EmailSender(toEmail, subject, body, emailLogger);
}
}
Now the problem with this is that we end up creating a whole lotta factory classes, and people don't always know to use them (they sometimes new them up themselves). What are the biggest negatives of coding the class like this:
public class EmailSender
{
EmailLogger logger = IoC.Resolve<ILogger>();
internal EmailSender(string toEmail, string subject,String body)
{.....}
}
Pro: we now can use the constructor safely without needing a factory class
Con: we have to reference the Service Locator (I'm not worried about testability, its easy to use a mock container as the backing service for the container).
Is there some big stinker of a reason out there why we shouldn't do this?
edit: after a bit of thought, I twigged that by having a private constructor, and by nesting the Factory class, I could keep the implementation and factory together, and prevent people from creating classes improperly, so the question has become somewhat moot. All the points points about SL being dirty, are of course true, so the solution below keeps me happy:
public class EmailSender
{
public class Factory
{
ILogger emailLogger;
public Factory(ILogger emailLogger)
{
this.emailLogger = emailLogger;
}
public EmailSender Create(string toEmail, string subject, string body)
{
return new EmailSender(toEmail, subject, body, emailLogger);
}
}
private EmailSender(string toEmail, string subject,String body, ILogger emailLogger)
{
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的——这很糟糕。
多余的,你不应该这样做
写下它们。
另一个甚至更重要的方面,
是你的组件绑定到
您的服务定位器。
您现在无法实例化它们
就像那样 - 你需要一个
完全设置服务定位器
每次需要使用时放置
,
遍布你的代码库
这不是一件好事,因为
当你想要改变某事时
你必须在多个地方寻找。
yes - it is bad.
superfluous and you shouldn't have to
write them.
Another, even more important aspect,
is that your components are tied to
your service locator.
You're now unable to instantiate them
just like that - you need a
completely set up service locator in
place every time you need to use a
component.
sprinkled all over your codebase
which is not a good thing, because
when you want to change something,
you have to look in multiple places.
我能想到的最大原因(不只考虑一般服务定位器的问题)是,这不是我作为您的班级的用户所会的期望。
元讨论:
某些 DI 框架(例如 Guice)将 为您构建工厂。
有些人主张将“新的”来自“可注射的”。
The largest reason I can think of (without just looking at issues with Service Locators in general) is that it is not what I as a user of your class would expect.
Meta discussion:
Some DI frameworks (Guice for example) will build the factory for you.
Some people advocate separating the "newable" from the "injectable".
我不太确定克日什托夫给出的这个强烈的“这很糟糕”的答案。我认为其中存在一些权衡和偏好,但没有绝对将它们归类为坏或好。
我认为使用服务定位器确实将您的依赖项隐藏在类中,而不是通过构造函数公开它们。在我看来,这很不方便,因为直到在未配置的情况下调用服务定位器之前,您不会知道您的类缺少某些内容。
但 DI 并没有摆脱这种代码黑暗。当您使用 DI 时,理解这些依赖项如何在您的构造函数中“出现”(DI 魔法)确实并不明显。通过使用 SL,您至少可以看到这些依赖项来自何处。
但是,当测试一个公开其构造函数的依赖项的类时,您(几乎)不会错过它。使用服务定位器时情况并非如此。
我并不是说克日什托夫错了,因为我最同意他的观点。但我很确定使用服务定位器并不一定是一个糟糕的“设计”,而且肯定不仅仅是糟糕。
菲尔
I'm not quite sure about this strong "it is bad" answer gave by Krzysztof. I think there's some trade-off and preference in there without absolutely categorizing them as bad or good.
I think using a service locator is indeed hiding your dependencies inside the class instead of exposing them through constructors. And it is an inconvenient in my opinion because you will not know your class is missing something until the service locator is called without being configured.
But the DI thing is not free of that kind of code darkness. When you use DI, it is really not obvious to understand how those dependencies just "appeared" (DI magic) in your constructor. By using a SL, you at least can see where those dependencies are coming from.
But still, when testing a class that expose those dependencies on her constructors, you (almost) can't miss it. That is not the case using a service locator.
I'm not saying Krzysztof was wrong, because I agree with him for the most. But I'm pretty sure using a service locator is not necessarily a bad "design" and certainly not simply bad.
Phil