4

Possible Duplicate:
Does “Inversion of Control” promote “Anemic Domain Model”?

My impression is that projects using a DI framework like Spring or Guice tend to lose their object orientation and degenerate to a purely procedural design:

  1. DI not only centralizes the question of which implementation of an object to use but also manages the life-cycle of these objects. Many OO design patterns, however, rely on the ability of business logic to wire objects. I wouldn't know, for example, how to implement a Strategy Pattern in Spring because the decision which of the concrete strategies to use, is statically determined by the configuration of the application instead of a piece of code. Same goes for decorator, composite, observer, ...

  2. The reason above leads to a design where functionality and data are separated. Because you don't want to let the DI container decide when data is created, you split any code off the data in order to have only this part managed by the DI container. This is contrary to the idea of OO where code and data should reside together in one unit. This breaks the encapsulation of data because all fields of the data "beans" are exposed by public getters and setters.

  3. You can't use polymorphism anymore because code is not linked to its data anymore and the "virtual functions" can't work. This leads to those instanceof cascades that we all know we shouldn't use. Also, we lose all those designs that rely on polymorphism.

  4. The DI container will inject managed beans only in objects which are also managed by it. So you can't mix managed beans and normal objects because there can't be any "non-managed gaps" in the reference chain of managed beans. So once you started with DI, you need to put all other code under the control of the DI container and you can't use normal objects anymore. I suppose that this is the reason why the separation of code and data is done so rigorously in DI projects.

I see that there are reasons why to use DI but is it really worth giving up so much? Don't people care about OO? Apart from this article, I can't find any discussions on this topic.

Or is it just me who doesn't understand how to do this properly? Any ideas how the four points above can be tackled with?

Appendix 1: Explanation of strategy pattern

I refer to the example here. Let's assume there's a DI version of it where all implementations of Strategy and the Context are injected to StrategyExample. Then StrategyExample doesn't decide anymore which Strategy to inject into Context but the configuration would have already decided which implementation to inject into Context. So yes, DI heavily applies the Strategy Pattern. But it always does so statically.

3
  • You are correct that you've found a duplicate question, because you (and the author of the article you linked to) are both confusing OO with Rich Domain. They're not the same thing. An anti-OO design pattern is Transaction Script, not Anaemic Domain.
    – pdr
    Jan 19, 2013 at 19:11
  • "Rich Domain" appears to me as a name for normal classes invented by people who are obsessed with EJB or Spring and don't want to admit that they drifted into the procedural world and are not programming OO anymore.
    – Wolfgang
    Jan 22, 2013 at 15:54
  • Then you need to look again.
    – pdr
    Jan 22, 2013 at 17:12

2 Answers 2

0

DI not only centralizes the question of which implementation of an object to use but also manages the life-cycle of these objects. Many OO design patterns, however, rely on the ability of business logic to wire objects. I wouldn't know, for example, how to implement a Strategy Pattern in Spring because the decision which of the concrete strategies to use, is statically determined by the configuration of the application instead of a piece of code. Same goes for decorator, composite, observer, ...

That's simply not true. I think strategy is a prime example for one pattern that actually benefits from DI and is by itself (with or without container) the prototype of DI. You can export as many strategies as you like and pick the one that you want in your business logic. Also, no one, I repeat nothing and no one stops you from instantiating a known strategy by yourself to override the strategy that was injected.

The reason above leads to a design where functionality and data are separated. Because you don't want to let the DI container decide when data is created, you split any code off the data in order to have only this part managed by the DI container. This is contrary to the idea of OO where code and data should reside together in one unit. This breaks the encapsulation of data because all fields of the data "beans" are exposed by public getters and setters.

I see your point but you can easily resolve this by using a factory pattern. In the simplest way, you can instantiate your object yourself and then have it built up and wired by the factory (calling and hiding the container's implementation).

You can't use polymorphism anymore because code is not linked to its data anymore and the "virtual functions" can't work. This leads to those instanceof cascades that we all know we shouldn't use. Also, we lose all those designs that rely on polymorphism.

I totally don't see your point here. Dependency Injection done right is a really good example for the power of polymorphism by itself.

The DI container will inject managed beans only in objects which are also managed by it. So you can't mix managed beans and normal objects because there can't be any "non-managed gaps" in the reference chain of managed beans. So once you started with DI, you need to put all other code under the control of the DI container and you can't use normal objects anymore. I suppose that this is the reason why the separation of code and data is done so rigorously in DI projects.

Yes, but this can easily be resolved with a factory approach. You totally can build software that runs with or without a DI container by using a simple factory.

8
  • 1
    Re. 1: What you describe is not the strategy pattern. Re. 2: My observation is that DI projects don't use such factories but use code-only and data-only classes. It's not seen as a bad thing by people who advocate DI. Re 3: I was interested in how this "done right" actually looks like :). Re 4: If such factories are able to inject managed objects to the new object they create, they cause the problem I described. If they don't inject managed objects, they are off-topic.
    – Wolfgang
    Jan 19, 2013 at 17:23
  • @Wolfang: Why don't I describe the strategy pattern? See also this thread: programmers.stackexchange.com/questions/135914/… Also, I think such factories can hide the DI and create the objects in another way and do not create the problem you described!
    – Falcon
    Jan 19, 2013 at 17:28
  • I've added an explanation to the question. Was too long for the comments here.
    – Wolfgang
    Jan 19, 2013 at 17:46
  • Also see here: stackoverflow.com/questions/4176520/…
    – Wolfgang
    Jan 19, 2013 at 17:50
  • 1
    To avoid turning this into a chat: an explanation or example of how one of the mentioned patterns can be implemented with injected objects would help.
    – Wolfgang
    Jan 19, 2013 at 18:44
0

I don't think DI throws OO principles out the window; in may ways, DI is dependent on them.

I'll keep this answer generic, since most DI frameworks borrow very heavily from one another. The concepts are similar, though some frameworks have features that others don't. I will assume, however, that the framework accepts external configuration that is read at runtime (like XML or properties file(s)), possibly in addition to compile-time configuration (like attributes).

In a DI world, you become more heavily dependent on interfaces and composition. For example, say your application needs to get a price of a product, and that price might come from a CSV file, or a database, or a REST service. You can define the interface as something like

public interface PricingService {
  public int getPrice(String productId);
}

with implementations

public class FilePricingService implements PricingService {
  public void setParser(Parser parser) { ... } // and getter
  public void setPriceFile(File file) { ... }  // and getter

  private Map<String, Integer> prices;
  public void init() {
    getParser().parse(getPriceFile());
     prices = ...;
  }

  public int getPrice(String productId) {
    return prices.get(productId);
  }
}

A decent DI system would be able to introspect the property types of this object, and call the setters with the appropriate objects. When an object is first referenced, the framework looks up all of the depended-upon objects, and calls the object's init() method if it has one. For example, a property file based configuration (just to make the example simpler) may look like:

# Application.properties
class=...
pricingService=CSVPricingService

# CSVPricingService.properties
class=FilePricingService
parser=CSVParser # defined elsewhere
priceFile=/path/to/prices.csv

# XMLPricingService.properties
class=FilePricingService
parser=XMLParser # defined elsewhere
priceFile=/path/to/prices.xml

So at runtime, the configuration can be changed without having to recompile anything. The framework knows that the FilePricingService is expecting priceFile t be a File, so it invokes its own converters to automagically create a File from the filename that was provided. It knows that Parser is not a simple type, so it assumes this is a reference to another DI-managed component, and looks up that component. This is especially handy if you don't have the source to the components you want to use. Extending the example:

public class RESTPricingService implements PricingService {
  public void setEndpointURL(URL endpoint) { ... }

  public int getPrice(String productId) { ... }
}

etc. The idea is that wherever something needs a PricingService, it doesn't have to care what the implementation class is. Say you had a requirement drop on you that says "pick the lowest price from two different CSV files and the REST service". The only additional code you'd have to write is a composition class that uses these other existing pricing services:

public class LowestPricingService implements PricingService {
  public void setPricingServices(List<PricingService> services) { ... }

  public int price(String productId) {
    int lowestPrice = Integer.MAX_VALUE;
    for(PricingService s : getPricingServices()) {
      int p = s.getPrice(productId);
      if(p < lowestPrice) {
        lowestPrice = p;
      }
    }
  }
}

And you can create a configuration file that looks like

# FileOnePricingService.properties
class=FilePricingService
parser=CSVParser
file=/path/to/file/one.csv

# FileTwoPricingService.properties
class=FilePricingService
parser=CSVParser
file=/path/to/file/two.csv

# RESTPricingService.properties
class=RESTPricingService
endpointURL=https://example.com/price

# FilesAndRESTPricingService.properties
class=LowestPricingService
pricingServices=FileOnePricingService,FileTwoPricingService,RESTPricingService

and tell the application to use that FilesAndRESTPricingService in its configuration.

This system is dependent on many OO priciples. Polymorphism/inheritance, encapsulation, etc., are all in use here. Java makes some of this a lot more verbose, with boilerplate getters and setters everywhere, but that's a deficiency of the language, not of the DI pattern. (Imagine a Java with macros that allow you to say { property File theFile; } which would create the getter/setter code).

The benefit of using a DI system like this is the framework handles a lot of the configuration for you. A good DI system would also provide a way for you to inspect the runtime configuration of the objects via some UI. At runtime, if you needed to change the list of PricingServices, for example, the framework UI should be able to show you all of the registered implementations, and let you change their dependencies on the fly.

Systems like this exist, and have existed for over a decade, in real-world production settings.

1
  • 1
    You're describing how a DI framework works, not how the problems I describe can be solved. I don't challenge that a DI framework internally uses OOP. I challenge that a program that uses a DI framework can be OO. But you make a good point that Java is not a suitable language for DI-based systems. Which supports my point that it's not intended for DI, but for OO.
    – Wolfgang
    Jan 19, 2013 at 17:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.