Wikipedia explains:
A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it.
But studying AngularJS's DI implementation, I noticed it includes many different structures to be injected such as:
- service
- value
- factory
- provider
- constant
How do these things other than "services" fit into the Dependency Injection pattern?
-
1The wikipedia article is incorrect in suggesting that a dependency can only be a service. As you point out, there are plenty of other possibilities.Jules– Jules09/27/2015 15:30:27Commented Sep 27, 2015 at 15:30
-
1I think this is mostly an issue of terminology. You can see a "value" as a special kind of service that provides a value, a "factory" as a service that produces objects and so forth if so you want.5gon12eder– 5gon12eder09/27/2015 16:50:35Commented Sep 27, 2015 at 16:50
3 Answers 3
Everything fits into the Dependency injection pattern.
When ol' Wirth was proclaiming that every Pascal program should begin with
PROGRAM ProgramName(INPUT,OUTPUT)
it was dependency injection that he had in mind, albeit in a very rudimentary form, and we did not even have a word for it yet, so generations upon generations of programmers never understood it, and instead copied it from program to program as some apocryphal words that just had to be written this way for the magic to work.
Explicitly passing the input stream and the output stream to your program instead of having your program rely on some pre-determined, fixed, globally available entities is, in a sense, dependency injection.
Allegedly for the sake of convenience, modern languages offer many globally available features that are at your disposal simply with an #include
or a using
or an import
statement. These import statements do not import just interfaces and primitive constants; they also import entire subsystems declared as public static
, ready to be used by anyone.
Does your sorting routine sometimes become bored while sorting, and feels the need to check out its facebook account? Just have your sorting routine import
the io.net
package from the SDK, and it can now access any gossip all over the entire planet.
Do your web services feel that their job is kind of lackluster and they'd prefer to do something more glamorous while waiting for the remote host to respond? They can just import java.awt
from the SDK, and display some slick 2D graphics for the amusement of whoever happens to walk by the server console at that moment.
All this insanity exists because the concept of dependency injection has not yet gained the traction it deserves.
Everything could, and in my opinion should, be done using dependency injection; instead of your console application's entry point being
int main( String args[] )
it could be
void main( ConsoleRuntimeEnvironment environment )
in which case you would be invoking the environment to get your arguments, and invoking it again to exit returning a result code.
If your application is a graphics application, it could be
void main( Graphical2DRuntimeEnvironment environment )
If it is a web service it could be
void main( WebServiceRuntimeEnviroment environment )
...and so on and so forth.
Whatever services, features, libraries, call them what you like you need, you should be able to obtain by asking your environment for them, and the public static
keyword combination should be prohibited for anything but primitives.
But while nobody enforces this style of doing things, nothing prevents you from doing it voluntarily. You can inject anything as a dependency. Want some examples that you can use right now in applications that are currently sitting in your hard drive?
Come up with a Logger
interface and pass it to all of your classes instead of magically using some omnipresent, omnipotent, and ultimately evil, statically available logging facility.
Come up with a Clock
interface and pass it to any code that needs the current time, so that you do not obtain the current time using new Instance()
, thus making your time-dependent code testable.
Come up with a FileSystem
interface and pass it to any code of yours that needs to deal with files, so that it does not use the new
operator to directly instantiate classes from java.io
. This way, your file-manipulation code can be tested using a filesystem implemented in memory, so your tests can be much faster.
And the list goes on.
A Dependency is any piece of logic your code depends on. Its inversion of control because the alternative method of implementing the logic would be
dependency.Logic(myObject)
vs
myObject.Logic(dependency)
the first requires that dependancy knows about and has access to the properties of myObject, where as the second allows you to construct and use myObject in an OOP style way and still keep the dependency logic in a seperate class
Any object can be registered with DI. The various types of registration methods in angular are simply conventions. .service
doesn't really have to register a service. Nor does .controller
have to register a controller. Angular is targeted at user interface, so its registration methods target predefined types of components which are commonly used in angular.
All of these methods just register a function / object, and it doesn't matter if a service is really registered with .service
. The only differences between them (generally) are:
- Whether the object is new every time (called the object's lifetime here)
- How the object is created
Lifetime
So an object registered with .service
is created new only once during the whole application. That same object is kept in memory (with all its current values) and injected every time another component asks for it. Changes made by ComponentA can be seen by ComponentB and vice versa.
But a .controller
object is created new every time before it is injected. ComponentA and ComponentB get their own copies of the object. If ComponentA sets a value on the controller, those changes only belong to ComponentA. ComponentB will never see them.
That's the difference in lifetimes. (In other frameworks there are additional lifetimes. Not sure if angular has more than these two.)
Object Creation
.service
and .factory
are both "new only once", but they differ in how the object is created. For a service, the angular framework uses new
to create the object.
var createdObject = new MyService();
But a factory is called as a method.
var createdObject = FactoryFunction();
Other Frameworks
Many other DI containers are used more generically. Instead of having different registration methods to specify a combination of lifetime and creation, they allow you to provide them discretely when you register the object. Consider the following.
public class WarriorModule : NinjectModule
{
public override void Load()
{
// IWeapon will be created with: new Sword()
// the same Sword will be used for every IWeapon requested
// similar to angular's .service registration
this.Bind<IWeapon>().To<Sword>().InSingletonScope();
// will be created by calling a function
// similar to .factory registration
this.Bind<IWeapon>().ToMethod(context => new Sword()).InSingletonScope();
// new sword created every time it is injected for IWeapon
// similar to .controller registration
this.Bind<IWeapon>().To<Sword>();
}
}