Dependency injection discourages object-oriented programming?

0 Flares 0 Flares ×

Suppose you have a Product entity and that you want to implement a method which sends the product to a customer (let’s call it ship). For that method to work, you need a service for calculating prices and a goods transporting service. To wire the services nicely, the natural choice nowadays is a DI framework (Seam, Guice, Spring, CDI, …).

Chances are high that to implement the above scenario, you would create a ProductService or a ProductManager (or if you have more specialized services, a ProductShipper) which would look more or less like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ProductService {
   // Here we can use setter injection, constructor injection, etc., doesn't matter
   @Inject private PriceCalculatorService priceCalculator;
   @Inject private TransportService transport;
 
   public void ship(Product product, Customer where) {
      int price = priceCalculator.calculate(product);
      // (...)
      transport.send(address, product);
   }
 
   // (...)
}

The main problem with that solution is that the product is always passed as an argument.

Check in your project: if you’re using DI, and you have an X entity, do you have an XService or XManager with lots of method where X is the first argument?

Wouldn’t it be better if you could create something like this:

1
2
3
4
5
6
7
8
9
public class ProductShipper {
   private final Product product;
 
   public ProductShipper(Product product) { this.product = product; }
 
   public void ship(Customer where) {
      // ???
   }
}

The previous way is more procedural: the service/manager is a set of procedures you can run, where the execution takes a product and a customer as arguments. Here we have a more OO approach, which has many benefits:

  • the product entity is encapsulated in the service
  • you can pass the shipper object around
  • you control where and how the shipper object is created
  • you can create multiple copies of shipper objects, for multiple products
  • etc

I’m not saying that achieving the above is not possible with a DI framework; only that DI encourages the ProductService approach: it’s just easier to code it procedurally, instead of e.g. creating a ProductShipper factory, passing all the needed dependencies to it and so on.

An obvious problem with the new approach is that if you create a ProductShipper object yourself, you won’t have dependencies injected. For DI to work, the object must be managed by the container.

Hence we come to some problems with DI frameworks:

  • for DI to work, the objects must be managed by the container. Creating an object using some data not available upfront and having dependencies injected is not possible
  • there can only be one (managed) instance of a class available at a single “execution” (for web applications, this will be per one request)
  • managed objects are either stateless, or holders of “anemic” data objects; extra steps are required to create “rich” objects (if you have two Product entities, you won’t be able to create two ProductShipper objects with that products encapsulated, but you can create a ProductsToShipHolder managed object into which you can add the products to ship)

How to solve these problems?

I think a good starting point is something that I call object services. Using them, you can inject a provider, with which you can obtain a service, given an entity (or any other object). The good side is that injection into the obtained services works normally. Following our example, the code could look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class UserInterface {
   // OSP stands for Object Service Provider
   @Inject OSP<Product, ProductShipper> shipperProvider;   
 
   public void shipClicked() {
      shipperProvider.f(getProduct()).ship(getCustomer());
   }
}
 
// OS stands for Object Service
public class ProductShipper implements OS<Product> {
   private Product product;
 
   public void setServiced(Product product) { this.product = product; }
 
   // Here we can use setter injection, constructor injection, etc., doesn't matter
   @Inject private PriceCalculatorService priceCalculator;
   @Inject private TransportService transport;
 
   public void ship(Customer where) {
      int price = priceCalculator.calculate(product);
      // (...)
      transport.send(address, product);
   }
}

The good thing here is that shipperProvider.f(getProduct()) is a regular object, which has the product encapsulated. It can be freely passed around, and you can create multiple shippers for different products.

(Another good thing here is that object services support polymorphism, so you can get different services injected for different objects!)

However this implementation still has some drawbacks; for example for injection to work the objects still have to be obtained using the provider, so that they are managed by the container. In reality most objects that are created are not managed by the container. How to obtain dependencies there? Normally they are passed e.g. in constructors, but then if suddenly an object deep down the hierarchy needs a dependency we are in trouble, and end up adding a new constructor parameter all the way up till we get to a managed object.

So stay tuned for more … :)

Adam

  • Carlos

    In Spring-based applications, you can annotate your classes with @Configurable. When you instantiate a @Configurable class using ‘new’, dependencies are injected using AOP.

  • http://www.warski.org Adam Warski

    I didn’t know about @Configurable – thanks a lot for the pointer :).

    This would allow you to add a ship() method to the Product entity. However continuing down that road, you may end up with loads of unrelated methods there; so I think it’s better to create specialized object services (like ProductShipper).

    Another thing is that in my opinion the need to weave classes and enrich them with AOP indicates that either the framework or the language lacks something ;).

  • http://hamletdarcy.blogspot.com Hamlet D’Arcy

    There is a Guice mechanism around this as well, but it has been 2 years since I used Guice so I forget the mechanics. In our analysis at work we discovered that (at the time) Guice had better support for configurable beans than Spring, but that was 2 years ago.

    The popular DI frameworks definitely have solutions for this problem.

    “there can only be one (managed) instance of a class available at a single “execution” (for web applications, this will be per one request)”

    - This is definitely 100% not true for Spring. There can be many, many managed instances of a class. It comes down to how you use scope (prototype, request, singleton). I wrote my own scope once to support a “flash” scope in my non-web app and it was pretty easy to do.

  • http://www.warski.org Adam Warski

    As for the number of managed instances available. It’s true that you can create many instances (in CDI this could be done through the “dependent” scope, not sure how this is called in Spring – maybe prototype). However you have one instance per injection point (so one field in a class). What if you have a List<Product> and you want to convert that into a List<ProductShipper>? You cannot create instances on-demand, depending on how many products you have. So in that sense the instances are more or less singleton.

    One way around that is, as I write in the blog, to create a factory (or, in CDI, inject Instance<ProductShipper>; for each get() invocation on the instance, you will get a new managed object instance if the scope is dependent. Then you can set the product.) As I write – solving these problems with current DI frameworks is possible, but is not the “easy” way, hence as programmers are lazy, not the “encouraged” one.

    Thanks for the pointer, I’ll ready up on the Spring prototype scope.

  • http://blog.lidalia.org.uk Robert Elliot

    “in my opinion the need to weave classes and enrich them with AOP indicates that either the framework or the language lacks something”

    I couldn’t agree more; I really don’t like dependency injection by framework using annotations. Particularly with beans whose lifecycle is container managed! It just seems so extra-linguistic… I don’t want some framework getting in there and changing a constructor call to dynamically inject some value. Much better to use a factory method/class of some kind so that the injection is done within the confines of the language.

  • Pingback: Tweets that mention Dependency injection discourages object-oriented programming? @ Blog of Adam Warski -- Topsy.com

  • Pingback: Dependency injection discourages object-oriented programming? | Pirate Gaspard

  • http://www.warski.org Adam Warski

    On a related note, an interesting read is what the DI folks think about DI in general:

    Discussion about DI in Lift: http://www.mail-archive.com/liftweb@googlegroups.com/msg10451.html
    DI in Scala: http://scala-programming-language.1934581.n4.nabble.com/Dependency-injection-in-Scala-td1937406.html
    Jonas Boner on DI in Scala: http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html

    Reading these discussions/articles led me to think about the DI approach in Java, hence my blog; good to remember that annotations/setters are not the only way :).

  • Maciej Biłas

    Take a look at the Assisted Inject pattern in Guice. http://code.google.com/p/google-guice/wiki/AssistedInject

    It’s the solution I use for the problem. I think it’s cleaner than the Object Service as the Product reference (or whatever entity for that matter) is kept final for the lifetime of the object.

  • http://www.warski.org Adam Warski

    Thanks for the link. The reference in OS can be as well final, it’s just a matter of hacking Weld a bit more (the Object Services impl I made so far is kind of a proof of concept) ;).

    The assisted inject approach has the benefit that you can pass multiple objects to it. On the down side, it doesn’t support polymorphism in the same way (the original cause for creating OS is to create “polymorphic extension methods”).

    And with Assisted Inject still have some “boilerplate” code; if you split your logic into many classes, creating a factory for each of them may be a bit tireing :). That’s why I wrote that the problems can be solved (e.g. using factories), but it’s not the regular usage of DI frameworks, something you would do for simple cases.

  • http://blog.loof.fr nicolas de loof

    I like to use a builder and a fluent interface for such use cases :

    builder.with( product ).ship( dest );

    the builder is IoC-framework aware and can build a new ProductShipper (command pattern), enable dependency injection on it, assign the product, etc…

  • http://www.warski.org Adam Warski

    It’s quite nice, and it’s what I mean when writing:

    “I’m not saying that achieving the above is not possible with a DI framework; only that DI encourages the ProductService approach: it’s just easier to code it procedurally, instead of e.g. creating a ProductShipper factory, passing all the needed dependencies to it and so on.”

    My main problem with this solution is that you can end up with a lot of builders. Programmers (me included) are quite lazy and creating a builder for simple cases is normally too much … of course simple cases evolve into complex ones and you end up with bad design ;).

  • Dave

    “…either the framework or the language lacks something…”

    Java lacks a lot. It has an impoverished model of abstraction. That is why things like DI are less common in stronger languages: they’re simply not necessary.

    All these frameworks exist as “extra-linguistic” bags. AOP exists because it’s one of the few ways to augment run-time behavior.

  • Pingback: DI and OO: Assisted Inject in CDI / Weld @ Blog of Adam Warski

  • Pingback: Improving autofactories/assisted inject @ Blog of Adam Warski

  • Pingback: DI in Scala: Cake pattern @ Blog of Adam Warski

  • Bruno Navert

    Spring has the concept of a lookup method that allows you to create new (prototype) objects from a singleton factory-type class. Those objects can then have DI-injected services, but once built, non-DI state can be passed in. In other words you could have regular DI annotations (no AOP here) and a setProduct() method to enter the Product that this instance needs to wrap.

    Solves the problem you mentioned rather well, and only needs CGLIB for generating a subclass through bytecode manipulation (pretty standard now)

  • http://www.warski.org Adam Warski

    This looks very similar to assisted inject (Guice)/autofactories (see my later posts on DI and Weld), doesn’t it? Maybe you could post a small example on how you define such beans and their dependencies?

    But unfortunately it still doesn’t look to be typesafe (see the Scala solution) ;).

    Adam

  • Pingback: DI in Scala: Cake Pattern pros & cons @ Blog of Adam Warski

  • http://thoughtresults.com Saeed Neamati

    I think the most important problem of DI is that, it’s not for small projects. If you use it in a small project (wrong place) then it becomes an anti-pattern rather than being helpful at all. Where you don’t want to be extensible and dynamic, why should you use it?

    Another problem I have is that name of IoC. I never understand it. ;). DI is meaningful and you do “inject” something that the class is “dependent” upon. But Inversion of Control doesn’t make sense to me at all.

  • http://www.warski.org Adam Warski

    IoC is step one: making sure that a class isn’t responsible for creating its dependencies, but the dependencies are provided from outside. In other words: use constructor arguments ;). And it’s perfectly usable in small projects.

    DI is a step further, providing e.g. auto-wiring, containers etc. True that it makes no sense for small things.

  • Pingback: Missing OO and FP bridge in Scala @ Blog of Adam Warski

0 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 0 Email -- 0 Flares ×