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:
-
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, …
-
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.
-
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.
-
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
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
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