工厂模式和依赖注入的 Demeter 法则
我有一个关于依赖注入的问题。
说我想创建一个类 调用它,WebGetTask
WebGetTask 需要依赖于 HttpService
错误代码 1 代码:
private HttpService httpService;
...
List<WebGetTask> list = new ArrayList<WebGetTask>();
for(...)
{
list.add(new WebGetTask(httpService));
}
...
好的。 新的 WebGetTask 上创建之外
我知道这很糟糕,因为 httpService 被注入,但它从未使用过,除了在 错误代码 2 代码:
private WebGetTaskFactory webGetTaskFactory;
...
List<WebGetTask> list = new ArrayList<WebGetTask>();
for(...)
{
list.add(webGetTaskFactory.newTask());
}
...
我认为这样更好,因为我们使用工厂 但... 但是..
从我站的地方来看, 我理解了 在 WebGetTaskFactory 中 我们仍然注入一个 HttpService 并且没有对它做任何事情,除了创建一个新的 WebGetTask 的唯一目的,
所以 回顾一下 我的问题是 我如何设计一个工厂类(WebGetTaskFactory),当新对象需要其构造函数的依赖项(HttpService)时创建新对象(WebGetTask),而不是简单地注入和传递依赖项(HttpService)? 或者更确切地说,这是这样做的方法吗? 如果是这样,那么一切都很好,如果不是,那么请指导我如何正确使用 DI 和工厂模式。 谢谢。
I have a question regarding dependency injection.
say i want to create a class
call it, WebGetTask
WebGetTask would need a dependency to HttpService
bad code 1
Code:
private HttpService httpService;
...
List<WebGetTask> list = new ArrayList<WebGetTask>();
for(...)
{
list.add(new WebGetTask(httpService));
}
...
ok. i know this is bad, because httpService is injected, but its never used, except for the creation on a new WebGetTask
ok
bad code 2
Code:
private WebGetTaskFactory webGetTaskFactory;
...
List<WebGetTask> list = new ArrayList<WebGetTask>();
for(...)
{
list.add(webGetTaskFactory.newTask());
}
...
i think this is better, because we use a factory
but...
but..
from where i'm standing,
i can see that
in WebGetTaskFactory
we are still injecting a HttpService and not doing anything to it except for the sole purpose of creating a new WebGetTask
so
to recap
my question is
how do i design a factory class (WebGetTaskFactory), that creates new objects (WebGetTask) when the new objects require a dependency (HttpService) on their constructor without simply injecting and passing the dependency (HttpService) ?
or rather, is this the way to do it? if so, then it's all good, if its not, then please guide me to how to properly use DI and factory pattern.
thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我将假设您显示的代码是 DownloadManager 类的一部分,并且您通过构造函数注入依赖项。 在这种情况下,我希望将所有内容粘合在一起的启动代码如下所示:
DownloadManager 类只知道 IWebGetTaskFactory 接口。 它不知道 IHttpService,因此满足 Demeter 定律。
编辑:重新阅读您的问题后,您似乎担心自己没有在工厂中“使用”HttpService,除了将其传递给新的 WebGetTask 之外。 还行吧。 WebGetTaskFactory 和 WebGetTask 都需要一个 HttpService 实例来完成其工作。 这并不违反德墨忒尔法则。
I'm going to assume that the code you have shown is part of a DownloadManager class, and that you inject your dependencies via the constructor. In this case, I would expect the start-up code which glues everything together to look like this:
The DownloadManager class only knows about the IWebGetTaskFactory interface. It does not know about IHttpService, thus satisfying the law of Demeter.
edit: After re-reading your question, it seems that you are worried that you are not "using" the HttpService in your factory, except to pass it on to a new WebGetTask. This is OK. Both WebGetTaskFactory and WebGetTask need an HttpService instance to do their job. This is not a violation of the law of Demeter.
好的,在 LoD 下,传递实现对象(构造函数中的“插件”)并没有什么特别的错误。 重要的是,类的接口并没有告诉您太多关于该实现的信息。
如果您的 WebGetTask 接口依赖于 HttpService 的具体实现,那么就违反了 Demeter 法则。
这里的技巧是考虑 WebGetTask 的接口签名。 这个名称本身表明您并没有完全遵循德米特定律(或最少知识原则),因为您定义的类 (1) 被定义为特定于网络,而 (2) 是一个动词名词的。
现在,这两者都不一定是错误的,但如果你愿意的话,它们都是“面向对象的味道”,这表明你可能没有足够客观地思考。
那么让我们尝试“重构”设计。 首先,考虑一个没有与之关联的“web”的 GetTask。 然后,您可以在构建时或稍后构建一个服务对象并将其传入。如果它是 HttpService,那很好,但是您的类的用户不需要任何有关幕后内容的信息。
第二件事,让我们把它变成一个名词。 将其称为 TaskFactory(您的直觉引导您就在那里),它带有一个采用 IOService 的 ctor,我刚刚发明了 IOService,它是由 HttpService 实现的抽象接口。
现在,您已经有了(这是一种 Java/C++ 伪代码,不要对语法细节感到兴奋):
并且您可以通过编写来使用它
现在,我们至少了解 TaskFactory 的内部结构、IO 服务以及就此而言任务。
Okay, there's nothing specifically wrong under the LoD about passing an implementation object, a "plugin" in the constructor. What's important is that the interface of the class doesn't tell you much about that implementation.
If your interface to WebGetTask depends on the exact implementation of HttpService, that violates the Law of Demeter.
The trick here is to think about the interface signature of WebGetTask. The name itself suggests that you're not quite following the Law of Demeter — or principle of least knowledge — because you're defining a class that (1) is defined as being specific for the web, and (2) is a verb instead of a noun.
Now, neither of those is necessarily wrong, but they are both "OO smells" if you will, signs that you may not be thinking object-ly enough.
So let's try "refactoring" the design. First thing, think about a GetTask that has no "web" associated with it. You can then, either at construction time or at some later time build a service object and pass it in. If it's HttpService, that's fine, but the user of your class doesn't need any information about what's under the covers.
Second thing, let's make it a noun. Call it TaskFactory — your intuition was leading you right there — with a ctor that takes an IOService, which I've just invented as being the abstract interface implemented by HttpService.
Now, you have (this is sort of Java/C++ pseudocode, don't get excited about syntax details):
and you use it by writing
Now, we kow the minimum about the innards of TaskFactory, the IO service, and for that matter Tasks.
DI 有两种方式:第一种是构造函数,当只注入一两个对象时很有用,另一种是 setter 方式(实际上需要多少个 setter 就可以)。
如果您想对 DI 使用工厂方法,原则上它与基于构造函数的方法相同。
示例 1,对于构造函数 DI:
示例 2,对于 setter DI:
当您在创建行为可能不同但具有相同接口(即 LoD)的对象时需要使用一些更大的逻辑时,工厂方法最适合。 假设有一个基于工厂参数动态实现的 DownloadManager 接口。
示例3,创建逻辑封装到工厂方法中:
There are two ways of DI: the first one is a constructor one, which is usefull when only one or two objects are injected, and a setter one (actually as many setters as needed).
If you want to use a factory method for DI than in principle its same as a constructor based one.
Example 1, for a constructor DI:
Example 2, for a setter DI:
The factory method is best for when you need to use some greater logic when creating objects that may behave differently, but have the same interface, thus the LoD. Lets assume there is a DownloadManager interface implemented dynamically based on the factory parameter(s).
Example 3, creation logic encapsulated into a factory method: