Inversion of Control
If you don’t get IoC you’ll only ever use DI as a bloated factory pattern. Take converting between XML, JSON and TXT, where you new up a pile of things inside and switch on an enum to decide. But IoC would work fine here. Some people say IoC is what DI is really about, and I don’t think they’re wrong. Control is really about “who constructs the thing I’m going to use”. A fairly simple signal is who new’d the object I want to use. If I new’d it myself, control is in my own hands. If it was handed to me from outside, that’s IoC.
Take the code below. It injects, but the control still sits inside, since the outside uses an enum to control what the conversion method does internally. It looks like control is on the outside, but this is indirect, fake control. The real control is still inside, and all you handed over is a switching signal.
// Injected, but control is not inverted
class ConversionService {
public string Convert(string input, FormatEnum format) {
switch (format) {
case FormatEnum.XML: return new XmlConverter().Convert(input);
case FormatEnum.JSON: return new JsonConverter().Convert(input);
// Every new format means editing here. Open/closed principle broken outright.
}
}
}
Only the code below is real IoC. The method body answers only for the structure, the objects it needs are built entirely outside, and control is fully inverted.
interface IConverter {
string Convert(string input);
}
class ConversionService {
private readonly IConverter _converter;
// the dependency is injected from outside, the Service itself doesn't do the new
public ConversionService(IConverter converter) {
_converter = converter;
}
public string Convert(string input) {
return _converter.Convert(input);
}
}
// caller (control is outside)
IConverter xmlConverter = new XmlConverter();
var svc = new ConversionService(xmlConverter); // inject the dependency
I think the important thing here is that remote-controlling it and injecting from outside both look like control, but there’s a critical difference. The first is selection. The boundary is set inside the method, all you can do is pick from the options that already exist, and the control really is still inside. IoC lets go completely. It doesn’t care what you hand in, doesn’t care about the boundary, and answers only to the interface (the structure). XML, JSON, TXT are three things, and the remote control can only pick those three. IoC is unlimited, and as long as you comply you can pass anything. That’s real inversion of control, letting go and answering only to the interface.
To put it as a kitchen and a cook, the first one is a kitchen that comes with cooks, but only three of them, Sichuan, Hunan and Shanghainese. What if I want Henan food? Sorry, not on offer. IoC is kitchen provided, bring your own cook. Henan food? No problem. Northeastern? No problem. Western food? Of course, as long as you use Chinese kitchen tools, since the kitchen (the interface) is fixed. That’s where the key to inversion of control is. It gives you the most flexibility and abstraction you can get, but it isn’t unconstrained, and defining the interface is critical.
But there’s a trap here, which is treating generics as IoC and thinking you’ve inverted control, when the control is actually still inside:
class ConversionService<T> where T : IConverter, new()
{
public string Convert(string input)
{
var converter = new T(); // the type is decided from outside, but construction still happens inside
return converter.Convert(input);
}
}
var svc = new ConversionService<XmlConverter>();
Injecting the type from outside does fix the bloat of the enum version, where you have to edit the main logic every time, and it abstracts that part out too. But the new still happens inside, so control is still inside, and nothing is inverted here. There’s another trap as well: constructing the class with new() means whatever gets passed in must have a parameterless constructor. As soon as it takes parameters, this part won’t compile. If the method itself needs parameters, this part can only go through the constructor, and it ends up very bloated.
Translation note. I wrote this in Chinese. This English version is an LLM translation, so the wording is not mine even though the thinking is. Original: 控制反轉.