工厂模式和依赖注入的 Demeter 法则

发布于 2024-07-17 14:25:12 字数 995 浏览 6 评论 0原文

我有一个关于依赖注入的问题。

说我想创建一个类 调用它,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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甲如呢乙后呢 2024-07-24 14:25:12

我将假设您显示的代码是 DownloadManager 类的一部分,并且您通过构造函数注入依赖项。 在这种情况下,我希望将所有内容粘合在一起的启动代码如下所示:

IHttpService httpService = new HttpService();
IWebGetTaskFactory webGetTaskFactory = new WebGetTaskFactory(httpService);
IDownloadManager downloadManager = new DownloadManager(webGetTaskFactory);

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:

IHttpService httpService = new HttpService();
IWebGetTaskFactory webGetTaskFactory = new WebGetTaskFactory(httpService);
IDownloadManager downloadManager = new DownloadManager(webGetTaskFactory);

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.

顾冷 2024-07-24 14:25:12

好的,在 LoD 下,传递实现对象(构造函数中的“插件”)并没有什么特别的错误。 重要的是,类的接口并没有告诉您太多关于该实现的信息。

如果您的 WebGetTask 接口依赖于 HttpService 的具体实现,那么就违反了 Demeter 法则。

这里的技巧是考虑 WebGetTask 的接口签名。 这个名称本身表明您并没有完全遵循德米特定律(或最少知识原则),因为您定义的类 (1) 被定义为特定于网络,而 (2) 是一个动词名词的。

现在,这两者都不一定是错误的,但如果你愿意的话,它们都是“面向对象的味道”,这表明你可能没有足够客观地思考。

那么让我们尝试“重构”设计。 首先,考虑一个没有与之关联的“web”的 GetTask。 然后,您可以在构建时或稍后构建一个服务对象并将其传入。如果它是 HttpService,那很好,但是您的类的用户不需要任何有关幕后内容的信息。

第二件事,让我们把它变成一个名词。 将其称为 TaskFactory(您的直觉引导您就在那里),它带有一个采用 IOService 的 ctor,我刚刚发明了 IOService,它是由 HttpService 实现的抽象接口。

现在,您已经有了(这是一种 Java/C++ 伪代码,不要对语法细节感到兴奋):

 class Task { ... }
 class TaskFactory {
    public TaskFactory(IOServer svc){...}
    public Task get(){...}
 }

并且您可以通过编写来使用它

 TaskFactory fac = new TaskFactory(new HttpService());
 Task tsk = fac.get();

现在,我们至少了解 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):

 class Task { ... }
 class TaskFactory {
    public TaskFactory(IOServer svc){...}
    public Task get(){...}
 }

and you use it by writing

 TaskFactory fac = new TaskFactory(new HttpService());
 Task tsk = fac.get();

Now, we kow the minimum about the innards of TaskFactory, the IO service, and for that matter Tasks.

春庭雪 2024-07-24 14:25:12

DI 有两种方式:第一种是构造函数,当只注入一两个对象时很有用,另一种是 setter 方式(实际上需要多少个 setter 就可以)。

如果您想对 DI 使用工厂方法,原则上它与基于构造函数的方法相同。

示例 1,对于构造函数 DI:

list.add( new WebGetTask( httpService ) ) ;

示例 2,对于 setter DI:

WebGetTask webGetTask = new WebGetTask();
webGetTask.setHttpService(httpService);
// set other dependencies
list.add(webGetTask);

当您在创建行为可能不同但具有相同接口(即 LoD)的对象时需要使用一些更大的逻辑时,工厂方法最适合。 假设有一个基于工厂参数动态实现的 DownloadManager 接口。

示例3,创建逻辑封装到工厂方法中:

public static DownloadManager createDownloadManager(HttpService httpService){

    if(null!=httpService){
      WebGetTask webGetTask = new WebGetTask();
      webGetTask.setHttpService(httpService);
      // set other dependencies
      return new DownloadManagerImpl1(webGetTask);
    } else {
      return new DownloadManagerImpl2();
    } // if-else
}

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:

list.add( new WebGetTask( httpService ) ) ;

Example 2, for a setter DI:

WebGetTask webGetTask = new WebGetTask();
webGetTask.setHttpService(httpService);
// set other dependencies
list.add(webGetTask);

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:

public static DownloadManager createDownloadManager(HttpService httpService){

    if(null!=httpService){
      WebGetTask webGetTask = new WebGetTask();
      webGetTask.setHttpService(httpService);
      // set other dependencies
      return new DownloadManagerImpl1(webGetTask);
    } else {
      return new DownloadManagerImpl2();
    } // if-else
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文