My previous post glanced over the details behind the dependency management framework that I have started to build. However the intention of this post is to rectify that and go through the core parts of the code in more detail. Hopefully giving you a better idea of how it currently works.
There are two main parts to the solution; the Container and the IHasDependencies interface.
The Container class is responsible for providing an instantiated class when a particular interface is requested. The container class uses an internal map to know which classes map to which interfaces, this map is what gives the users the flexibility to change dependencies without changing code as everything is controlled from it’s contents.
Users are provided with two ways to build the map in the Container; either programmatically or via an external mapping file. To programmatically add a mapping simply call the addMapping method passing in a ClassMap. The ClassMap expects Type parameters for the interface and class that you want to add; this is to try and make the code clearer and remove the chance of typos messing things up. To load up the map using an external mapping file a simple call to loadMappFile passing in the name of the static resource that is the file will populate the map. The map file itself is currently just a JSON representation of a ListI had to use a different class as you can’t deserialize the Type class – although as you can see from the code ultimately the ClassMapFileEntry is used to populate a ClassMap class before being added to the map.
There are two ways to build the map as I think they lend themselves to different use cases: the programmatic method being great for quickly pulling together some mock classes in a test scenario, whilst the mapping file gives much more flexibility at runtime or when moving code between environments. There are clearly crinkles in this to work though – multiple mappings for the same interface, conditional mappings, ensuring the class implements the interface it’s mapped to are just a few that fall off the top of my head.
Given the map the container is now in a position to provide users with the classes they need for given interfaces and it is the getClass method on the container that provides this functionality.
This method is probably a bit longer than it needs to be at the minute but cutting through the chaff you can see that it can be broken down into three basic functions:
- get the name of the class needed from the map
- instantiate the class
- get any dependencies that class requires
Whilst the heart of the users experience this is the simplest part of the whole solution in my mind. Getting the classname form the map is self explanatory. The instantiation makes use of the JSON parser trick to get the class; the only thing to remember here is that the constructor of the class isn’t called. And the final part, getting dependencies, is really just a recursive call to this method: rinse and repeat. OK it’s a little more involved than that but not much and falls nicely into the next section.
One of my biggest goals at the outset of this process was to not have to worry about child classes and their dependencies. I want to be able to say to the container: “give me a working one of those – and don’t bother me with how”. This is where the simple IHasDependency interface comes to the rescue.
As you can see the interface is extremely simple. The key to the process is the map returned but getDependencies; the key set of the map is used by the container to know which interfaces to request classes for in the recursive call. The values are where the container puts the instantiated classes to pass back to the class requesting the dependencies. This works because non-primitive types are passed by reference. It would obviously be possible to have a member on the interface that was this map but we would run into problems with being able to populate it in classes that are instantiated via the JSON trick.
The second method in the interface is gotDependencies and at first look seems a bit odd. Looking back at the container class you can see that this method is called after all of the dependencies have been resolved and populated in the map. So, why is it called? It is there to give the class implementing the interface the chance to take it’s new found classes out of the map and assign them to internal members; after all no one wants to be referencing members from a map it just feels ugly – although if that’s what you want then knock yourself out!
An example of a simple class that implements the IHasDependencies interface would look something like this.
This class has a dependency on a IHttp implementation. To declare that it has this dependency it implements the IHasDependencies interface. In the getDependencies method is creates a new map and adds to it the interfaces for which it needs concreate classes, in this case it’s just IHttp. This now means that when the container instantiates the ParentClass class it will know it has dependencies that need full filling (the IHasDependencies interface) and by calling the getDependencies method the container is able to get a list of required interfaces and can populate the map to return them to the ParentClass.
You can also see in the ParentClass the implementation of the gotDependencies method; in this the IHttp class in the map is assigned out to a private member of the ParentClass class to allow it to be easily used elsewhere in the class.
Bringing it all together
Getting a fully loaded ParentClass is a simply a case of loading a map and then requesting the class from the container.
Two simple calls and obviously for each extra class you need it’s just a case of a call to getClass. There is some overhead associated with this in that you need to a build an maintain your mapping file and that you need to instrument your classes with the IHasDependencies interface but rarely do you get something for nothing.
What you go get however is a nicely de-coupled code base. And not only that it actually starts to become quite readable; getting a class with all necessary dependencies is one call in your code, all classes with dependencies are clearly labels and list all dependencies in one common place and the mapping is held in a centralised location. For me this framework looks like it will meet my needs.
Having said that I also think there is still work to be done; this is very much the seed of the idea and I think there are lots of improvements to be made as well as bells and whistles to add.
The Force.com project can be found on Github feel free to dive in and hack about with it.