Dependency Resolution

Thus far, we have refactored our code to be dependent on an interface instead of a concrete object. This is all well and good, but begs the question: "how do we get hold of an interface?" – or more correctly – "how do we get hold of the concrete class that is currently implementing this interface?". This is the essential question that Dependency Injectors seek to answer.

There are a number of different ways in which a class can get hold of another class that implements an interface.

Service Location

If the class itself requests a concrete object based on an interface, then this process is called "Service Location". In other words, the class is using a registry or helper to locate the service it requires. You could also describe this technique as "dependency requesting". A central registry holds a lookup table with all registered classes against their respective interfaces. When the interface is requested, the Service locator simply looks up what class instance is stored against the interface in its table, and returns the object from its registry.

Dependency Injection

If the act of creating an instance of a class can be handed over to some sort of framework, then this framework can work out what interfaces a class needs, and "inject" these dependencies during class instantiation. This injection of dependencies is also called assembly. In this case, an assembler class or framework would need to be able to query an object to find out what interfaces it is dependent on. Unfortunately, we do not have this ability in JavaScript or TypeScript, as all interfaces are compiled away. So, we cannot use TypeScript interfaces by themselves to implement dependency injection. If we were to implement dependency injection in TypeScript or JavaScript, we would need some sort of naming convention to flag to the assembler framework that we need a concrete object to replace an interface.

Dependency Injection is also referred to as Inversion of Control—as we are handing over control of creation of our classes, and the resolution of their dependencies—to a third party. By the time we receive an instance of our class, all of the services or dependencies have been "magically" filled in.

Service Location versus Dependency Injection

The ideas around the Service Location pattern were first introduced by Martin Fowler around 2004, in a blog titled Inversion of Control Containers and the Dependency Injection pattern (http://martinfowler.com/articles/injection.html). However, in his book, Dependency Injection in .NET, Mark Seeman argues that the Service Location pattern is in fact an anti-pattern.

Mark's take on Martin's original ideas are that it is too easy to introduce runtime errors, or to misunderstand the usage of a particular class, when Service Location is used. This is because figuring out what services a class uses, means reading through the entire class. He argues that a better way of using Dependency Injection, is to list all dependencies in the constructor function of a class, and let the service locator resolve each dependency, during class constructon. Most of Mark's examples seem to revolve around building and using APIs, where internals of a particular class cannot simply be read from the code, and using a class without knowing what services it depends on, can easily cause runtime errors.

While his ideas do certainly hold true, the solutions to this problem are all relevant to the .NET language—which has a key language feature that is unavailable in JavaScript— called Reflection. Reflection is the ability of a program—at runtime—to interrogate an object for information about itself, such as what properties it has, and what interfaces it implements or expects. Even though TypeScript provides the interface keyword, and does compile-time checking on these interfaces, all interfaces are compiled away in the resultant JavaScript.

This provides us with a serious problem. If a class is dependent on an interface, we cannot use this interface at runtime to look up the concrete implementation for the interface—because at runtime, this interface simply does not exist.

Angular uses a naming convention (a $ prefix) to provide dependency injection capabilities. This has been rather successful, although there are caveats and some work-arounds when using minification routines. Angular 2.0 also solves this problem by providing a custom syntax to denote places where dependencies need to be injected. Other JavaScript frameworks—such as ExtJs—provide a mechanism to create objects by using a global creation routine, which then allows the framework to inject dependencies. This ExtJs technique, unfortunately, is not very compatible with the TypeScript language syntax (see Chapter 5, Third Party Libraries where we discuss ExtJs).

Also, if we are not using Angular, Angular 2.0, ExtJs, or any other framework, then Dependency Injection is just slightly out of reach in standard JavaScript. Service Location, on the other hand, can be accomplished, and combined with TypeScript interfaces, can bring us all of the benefits of dependency resolution and therefore, modular programming.

We can also make a compromise in order to incorporate the ideas that Mark suggests—and limit our Service Location to object constructors. When writing libraries that use Service Location, we would need to clearly document what dependencies a particular class has—and how they need to be registered. Even popular .NET Dependency injection frameworks such as StructureMap still allow for Service Location techniques—although they are being deprecated.

For the purposes of this book, then, let's explore how to write a simple Service Locator and use it in our code to build a more modular application, and leave the argument about pattern versus anti-pattern to those languages that have the features to implement Dependency Injection naturally.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.195.251