The current programming community appears to be split on the concept of using Dependency Injection within projects and applications. Notice how I made Dependency Injection uppercase. This was done on purpose to highlight how strongly programmers feel about one way or the other on the subject. For the purposes of this article I will be referring to it as DI to keep things short.
Lets start from the beginning…
DI is the practice of separating your application into services and clients. Any object that may be used can be considered a service. Any object that uses other objects can be considered a client. The names have nothing to do with what the objects are for and everything to do with the role the objects play in any one injection. An injector fits in between the service and the client and returns the appropriate object based on a clients calls to a service.
It’s confusion to say the least.
Let me break it down into layman’s terms (well at least basic programmer terms)…
Using standard OOP structure, you call classes directly from other classes. Sometimes instantiating new objects and sometimes calling from an existing singleton through a static method. You call what you need directly and you build your code in a way that each object knows exactly what to expect from other objects.
DI uses a structure where classes are instantiated into a global variable or property within a container. When you need to access an object you call the global variable (usually through a framework). You are still able to access the same objects but you do not access anything directly. Your objects do not know exactly what to expect from the other objects.
You may be wondering why the heck would you ever want to add another level of complexity and extraction to your code? I have used DI extensively (sometimes forced against my will) and I highly prefer to only use it when absolutely necessary. I will explain why.
- Interchangeable services: Because you don’t call any objects directly, you can change one variable and now all calls can go to another class.
- Test-ability: If you call a class that makes calls to other classes you can change the global variable to a mock object for the called classes which isolates the class you are testing specifically.
- Multiple API: If you want to access multiple API interfaces with the same calls you can change one variable to access a new API.
- Code Reuse: In some situations you can easily bring over code into new projects without updating namespaces etc.
The Dark Side:
- Expensive: Every single call to a class requires another layer of extraction which means slower performance and in some cases higher memory usage.
- Framework Dependency: While you may love your framework, other programmers may not know how to use it. If a framework becomes stale you may be married to it.
- Inability To Trace Code: Removes all the nice code trace ability found in a modern IDE. As well as forcing you to search through code to find the keys used to access objects instead of object names.
- Proprietary Structure: Only yourself or you team knows how to navigate your code. Unless you create and keep up to date extensive documentation, no-one else will know how to use your code.
- Short Knowledge Life: Programming practices evolve or time. This gets even worse with DI. Next year you will have no idea what your code is doing because it’s untraceable.
- Linkage Unmanageable: DI forces any arguments to be passed to classes through the injector which makes changes hard to manage.
- Test-ability Creep: By using DI to test your classes its easy to forget to build properly testable classes.
- More Initial Work: It takes extra code and thought to setup calls with DI.
With all these things in mind I still find instances where using DI is preferred. One example where I have used it recently is within a Cordova application that features a browser demo. There are many API calls that must do different things based on the device. I use a DI container to hold the appropriate object based on if I am running on Android or on a browser then make my calls to the container. This keeps thing separate and allows a single call.
My strongest recommendation with DI is to use Interfaces. An Interface allows you to specify rules that your classes must follow. Most major languages support interfaces including PHP and Java. This will allow your objects to know what to expect when calling other objects from DI and prevent issues down the stack. Using DI without interfaces is like starting your bug tracking over from scratch each time you change an injector call.
But alas, 90% of the time I find DI to be a huge time suck filled with frustration. It seems way easier to build testable classes in the first place then to work with DI to make testing easier.
Since this a WordPress site you are probably wondering if you should use DI within a WordPress project? I would say almost never. The main reason is the wide variety of developers who will likely be exposed to your code. If you use DI (which like 2% of the WordPress community is) no-one else from the community will know how to work with your code. It will become a propriety internal application which will frustrate any developers trying to improve or contribute to your code.
Luckily WordPress has it’s own structure for injecting data or calls which is setup as a series of ‘actions’ and ‘filters’. If you need to inject something that structure will cover just about every use case. You will find no direct class DI within WordPress core PHP.