views:

466

answers:

3

hello all 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.

+1  A: 

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.

Charlie Martin
+6  A: 

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.

Wim Coenen
yes. worried about not "using" httpService inside webGetTaskFactory. so i inject a "httpService" object into a "webGetTaskFactory", "webGetTaskFactory" only ever uses "httpService" when it creates a new "webGetTask". and this is OK. cool. Is there a more "text-book" example for doing this? or is this the most common way to create objects that require a specific dependency on its constructor?
Titi Wangsa bin Damhore
If you think about it, the factory performs "run-time dependency injection". This is the natural way if you want to follow the dependency injection patterns for objects that are created dynamically at run-time, as opposed to design-time.
Wim Coenen
A: 

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
}
Azder