Chapter 13. Advanced Patterns

I hesitated when naming this chapter, Advanced Patterns. This isn't really about patterns that are more complicated or sophisticated than other patterns. It is about patterns that you wouldn't use very frequently. Frankly, coming from a static programming language background, some of them seem crazy. Nonetheless they are completely valid patterns and are in use within big name projects everywhere.

In this chapter we'll be looking at the following topics:

  • Dependency injection
  • Live post processing
  • Aspect oriented programming
  • Macros

Dependency injection

One of the topics we've been talking about continuously during this book is the importance of making your code modular. Small classes are easier to test, provide better reuse, and promote better collaboration for teams. Modular, loosely coupled code is easier to maintain, as changes can be limited. You may remember the example of a ripstop we used earlier.

With modular code of this sort we see a lot of inversion of control. Classes have functionality inserted into them through passing additional classes by their creators. This moves the responsibility for how some portions of the child class work to the parent. For small projects, this is a pretty reasonable approach. As projects get more complicated and dependency graphs get more complicated, manually injecting the functionality becomes more and more difficult. We are still creating objects all over the code base, passing them into created objects so the coupling problem still exists, we've just shifted it up a level.

If we think of object creation as a service, then a solution to this problem presents itself. We can defer the object creation to a central location. This allows us to change the implementations for a given interface in one place, simply and easily. It also allows us to control object lifetime so that we can reuse objects or recreate them every time they are used. If we need to replace one implementation of an interface with another implementation, then we can be confident that we need to only change it in one location. Because the new implementation still fulfils the contract, that is the interface, then all the classes that make use of the interface can remain ignorant of the change.

What's more is that by centralizing object creation it becomes easier to construct objects that depend on other objects. If we look at a dependency graph for a module such as the UserManager variable, it is clear that it has a number of dependencies. These dependencies may have additional dependencies and so forth. To build a UserManager variable, we not only need to pass in the database, but also ConnectionStringProvider, CredentialProvider, and ConfigFileConnectionStringReader. Goodness, that is going to be a lot of work to create instances of all of these. If we, instead, register implementations of each of these interfaces in a registry, then we need only go to the registry to look up how to make them. This can be automated and the dependencies automatically get injected to all dependencies without a need to explicitly create any of them. This method of solving dependencies is commonly referred to as 'solving the transitive closure'.

A dependency injection framework handles the responsibility of constructing objects. On application set up the dependency injection framework is primed with a combination of names and objects. From this, it creates a registry or a container. When constructing an object through the container, the container looks at the signature of the constructor and attempts to satisfy the arguments on the constructor. Here is an illustration of a dependency graph:

Dependency injection

In more statically typed languages such as C# or Java, dependency injection frameworks are commonplace. They usually work by using reflection, a method of using code to extract structural information from other code. When building the container, one specifies an interface and one or more concrete classes that can satisfy the interface. Of course using interfaces and reflection to perform dependency injection requires that the language support both interfaces and introspection.

There is no way to do this in JavaScript. JavaScript has neither direct introspection nor a traditional object inheritance model. A common approach is to use variable names to solve the dependency problem. Consider a class that has a constructor like so:

var UserManager = (function () {
  function UserManager(database, userEmailer) {
    this.database = database;
    this.userEmailer = userEmailer;
  }
  return UserManager;
})();

The constructor takes two arguments that are very specifically named. When we construct this class through the dependency injection, these two arguments are satisfied by looking through the names registered with the container and passing them into the constructor. However, without introspection how can we extract the names of the parameters so we know what to pass into the constructor?

The solution is actually amazingly simple. The original text of any function in JavaScript is available by simply calling toString on it. So, for the constructor given in the preceding code, we can do just do this:

UserManager.toString()

Now we can parse the string returned to extract the names of the parameters. Care must be taken to parse the text correctly, but it is possible. The popular JavaScript framework, Angular, actually uses this method to do its dependency injection. The result remains relatively preformat. The parsing really only needs to be done once and the results cached, so no additional penalty is incurred.

I won't go through how to actually implement the dependency injection, as it is rather tedious. When parsing the function, you can either parse it using a string-matching algorithm or build a lexer and parser for the JavaScript grammar. The first solution seems easier but it is likely a better decision to try to build up a simple syntax tree for the code into which you're injecting. Fortunately, the entire method body can be treated as a single token, so it is vastly easier than building a fully-fledged parser.

If you're willing to impose a different syntax on the user of your dependency injection framework then you can even go so far as to create your own syntax. The Angular 2.0 dependency injection framework, di.js, supports a custom syntax for denoting both places where objects should be injected and for denoting which objects satisfy some requirement.

Using it as a class into which some code needs to be injected, looks like this code, taken from the di.js examples page:

@Inject(CoffeeMaker, Skillet, Stove, Fridge, Dishwasher)
export class Kitchen {
  constructor(coffeeMaker, skillet, stove, fridge, dishwasher) {
    this.coffeeMaker = coffeeMaker;
    this.skillet = skillet;
    this.stove = stove;
    this.fridge = fridge;
    this.dishwasher = dishwasher;
  }
}

The CoffeeMaker instance might look like the following code:

@Provide(CoffeeMaker)
@Inject(Filter, Container)
export class BodumCoffeeMaker{
  constructor(filter, container){
  …
  }
}

You might have also noticed that this example makes use of the class keyword. This is because the project is very forward looking and requires the use of traceur.js to provide for ES6 class support. We'll learn about traceur.js file in the next chapter.

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

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