Inversion of Control in Software Engineering
What is Inversion of Control in Software Engineering?
Inversion of Control in Software Engineering (IoC) is essentially a strategy for decoupling software components. Basically, the purpose of IoC is to remove unnecessary dependencies (couplings) which might reduce flexibility and elegance of a system’s design. The term Inversion of Control in Software Engineering might sometimes appear applied to different design contexts. It is a term that comes from the 70s, when bottom-up parsing was considered an inversion of top-down parsing. It has roughly continued to be applied in multiple contexts. Some people think Inversion of Control in Software Engineering is a design strategy. For others, it’s a design pattern. After some articles dealing with OpenGL, in this post we will briefly discuss this topic of Software Engineering.
IoC Example
For instance, let’s suppose that we have a Text Editor class designed to modify and compose texts, in the style of programs such as Microsoft Word, Libre Office, Notepad, etc.
public class TextEditor { private MSWordReader wordReader; public TextEditor() { this.wordReader = new MSWordReader(); } ... public void LoadTextFile() text = wordReader.read(); } ... }
Our WordReader class is a component for reading a text document from a file in Microsoft Word format. Note that our TextEditor class creates an instance of MSWordReader in its constructor, and then uses it in methods such as LoadTextFile. Although it lacks elegance, this design will work well as long as the only type of document that our text editor accepts is exclusively a file conformant to Microsoft Word’s document format (in fact, there are several Word document formats, but for the sake of simplicity, let’s generalize). Now, we will likely want to add support for another text document format in the future. How to do that? For example, how to modifiy our text editor to load XML or LibreOffice documents? The following implementation would be a monstruosity:
public class TextEditor { private MSWordReader wordReader; private XMLReader xmlReader; private LibreReader libreReader; public TextEditor() { this.wordReader = new MSWordReader(); this.xmlReader = new XMLReader(); // etc } ... }
Beyond the twisted nature of the previous code, we might also have to face naming problems. For example, let’s note that the MSWordReader class has a “read” method, invoked in LoadTextFile. But it could happen that the designer of the XMLReader class independently decided that the equivalent method in XMLReader would be named “readFile”. In such case the code would be a hideous mess. We are losing control over document loading, because we depend on the instance or instances of readers that have been added to the class. A much better option is to decouple the document reader, inverting control.
Inverting the Control in the TextEditor class
First, we can recur to an ITextReader interface that all document readers must implement. Next, we’ll be providing a specific document reader in the constructor of TextEditor. In the ITextReader interface we would have a “read” method. And then, our TextEditor class would change to:
public class TextEditor { private ITextReader textReader; public TextEditor(ITextReader textReader) { this.textReader = textReader; } ... public void LoadTextFile() text = textReader.read(); } }
Way better! We have now transferred control to the client of the TextEditor class, which can decide which document reader to use. Notice the inversion: now the instance of the document reader (passed to TextEditor’s constructor) would be created before the instance of the TextEditor class. In our original approach, the TextEditor instance was first created and then the document reader (or readers) would be instantiated by the TextEditor.
Inversion of Control in Software Engineering: Other Context
There are many other ways to achieve this Inversion of Control in Software Engineering. For example, with callbacks, a very typical approach to design GUI systems. In a console application we could sequentially request data from the user: Input your name, (read name), Input your age, (read age), Input your place of birth, (Read place of birth), etc. By using callbacks we can invert the control because then we could allow the user to select the GUI text box for age and provide that data first, and then she could pick the text box for the name, and so on. Now control is not tightly determined and embedded in our application.
In closing, Inversion of Control in Software Engineering tries to decouple components, reducing dependencies and increasing design flexibility. If you are in the mood to read more about Software Engineering, I can recommend Lampson’s hints.