Saturday, October 17, 2015

Delivering Highly Configurable Libraries Using Dependency Injection



In the last post, I described usage of a factory pattern to provide highly configurable functionality. An alternate approach would be the usage of dependency injection(DI). At the time I designed these libraries, dependency injection platforms on Android were not as mature as they are today. Guice was starting to gain some popularity, but uses reflection and can impact runtime performance. With the advent of Dagger2, we have access to a static dependency injection solution that does not rely on run-time binding. 

Using Dagger2, I could replace the previous abstract factory pattern. I decided to explore this solution as an exercise. Note that I did not actually implement and compile this solution and humans are notoriously lousy compilers, so it's possible (likely) there are errors. However, it should illustrate the general approach.  This also assumes that you are generally familiar with usage of Dagger2.



As commented in the code. To override this behavior, the user could provide their own graph and data module and call ApplicationDispatchHandlerDaggerSample.registerHandlers() at runtime.

While some might argue that the DI solution with Dagger2 is cleaner, it would impose an additional constraint on each user of the NAC API to download and learn its usage. This is a relatively minor cost, but you could make the argument that in the interest of keeping NAC as easy to use as possible, the Dagger2 approach would add complexity with no real end-user benefit. A bigger issue with this approach is that, since we don't know which modules the user will want to  inject a-priori, we inject them all. This means that a user would have to specify handlers from within the NAC library, which  the user should not even need to know about, and which we have been keeping private and obfuscated. There are probably ways we could fix this by exposing methods to get the default implementation for a class, but this seems to just add more unintentional complexity. My conclusion is that, while DI is an important and highly useful pattern, for  this  specific use-case, the pattern we chose was an overall better approach.