Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Layered Architecture, Dependency Injection, and Dependency Inversion : Page 4

Building loosely coupled application architectures requires more than just separating your application into different layers.


advertisement
Dependency Injection with Service Locators
To completely eliminate the need for the presenter to be aware of which implementation of IEmployeeTask it should use, you must add a new element into the mix. In the realm of dependency injection, a service locator is simply an object that knows how to retrieve dependencies for other objects. In this scenario, it would be the role of the service locator to find an implementation of IEmployeeTask that the presenter can work with. All of you who have been reading along probably know the answer to the question, "Does this mean that my presenter will now have a dependency on this service locator?" Absolutely. Then you might ask, "Does this mean I should start changing the constructor of my presenter to also allow it to accept a service locator?" You could do that. Unfortunately, if other classes down the road want to utilize the services of the locator to resolve their dependencies, they too would require a constructor that allowed them to accept an interface to the service locator. This seems like a little too intrusive to the architecture and more work than is necessary.

The solution I am going to propose is one that you can use immediately if you want to start utilizing service locators in your own projects. It is also a solution that can scale when you start using full-blown dependency injection frameworks like Windsor Castle, a popular, open-source dependency injection framework. I am first going to create an interface for the service locator:

public interface IDependencyResolver : IDisposable { void RegisterImplmentationOf<T>( T component); T GetImplementationOf<T>(); }

The preceding code uses generic methods to avoid the need to cast when invoking the locator from client methods. I stated that I want existing classes to be able to consume the functionality provided by the service locator without a need to have an explicit parameter dependency on it. To accomplish this I will use a static class that will delegate all of its calls to any particular implementation of the service locator interface:

public static class DependencyResolver { private static IDependencyResolver resolver; public static void RegisterResolver( IDependencyResolver resolver) { DependencyResolver.resolver = resolver; } public static T GetImplementationOf<T>() { return resolver.GetImplementationOf<T>(); } }

Notice that the DependencyResolver class has a method called RegisterResolver which is passed an implementation of IDependencyResolver that it can forward its calls onto. What does this translate to from the perspective of the presenter? Instead of it needing to have knowledge of the EmployeeTask implementation of IEmployeeTask, it can now use the DependencyResolver class to locate an implementation of IEmployeeTask. This changes the constructors in EmployeePresenter from this:



Public ViewEmployeesPresenter( IEmployeeView view):this( view,new EmployeeTask()) { } public ViewEmployeesPresenter( IEmployeeView view, IEmployeeTask task)

To this:

public ViewEmployeesPresenter( IEmployeeView view):this(view, DependencyResolver.GetImplementationOf <IEmployeeTask>()) { } public ViewEmployeesPresenter( IEmployeeView view, IEmployeeTask task)

Notice that I am still making use of the greedy constructor that requires everything that the presenter requires. I can use this constructor from a testing perspective to verify the behavior of the presenter. In the constructor that the web page uses, the presenter uses the DependencyResolver and asks it to retrieve an implementation of an IEmployeeTask. Now, nothing in the presenter couples it to the EmployeeTask component!

I chose to couple the presenter to the service locator by using the static DependencyResolver class. Someone out there right now is screaming, "static classes are evil!" In addition, lots of people scream, "singletons are evil." In either case, they are not evil, as long as they allow for testability, which is typically not easy to do with traditional singleton or static class implementations. This scenario makes testability easy because the DependencyResolver static class does nothing more than delegate its work to an IDependencyResolver implementation (which you can easily fake at test time).

The advantage of using the interface (IDependencyResolver) is that when I am first starting down the service locator route, I may be content with creating my own service locator that gets configured at application startup. Listing 5 shows the code for such a locator. Here's an example that configures such a locator in a class invoked at application startup:

public class ApplicationStartupTask { public static void Initialize() { IDependencyResolver resolver = new CustomDependencyResolver(); resolver.RegisterImplmentationOf<IEmployeeTask> (new EmployeeTask()); DependencyResolver.RegisterResolver(resolver); } }

Notice that I explicitly tell the service locator when asked for implementations of IEmployeeTask to return the EmployeeTask object. This particular implementation also enforces the EmployeeTask to be a singleton in this application, even though it is not explicitly marked as a singleton. The final step registers the service locator with the DependencyResolver so that all clients can access its functionality.

If you want more functionality out of your service locator you may not want to code it yourself. By using the IDependencyResolver interface, you can easily switch to an implementation that leverages already existing dependency injection frameworks. Listing 6 shows an example of an IDependencyResolver implementation coded to work with Windsor Castle.

The value of coding to interfaces means that when (and if) I switch to Windsor (or another dependency injection framework), very little of my client code should change. In fact, the only change required to use Windsor is to change the ApplicationStartupTask class as follows:

public class ApplicationStartupTask { private static readonly string ContainerConfigurationPath = Path.Combine(AppDomain. CurrentDomain.BaseDirectory, "container.boo"); public static void Initialize() { IWindsorContainer container = new WindsorContainer(); BooReader.Read(container, ContainerConfigurationPath); DependencyResolver.RegisterResolver( new WindsorDependencyContainer(container)); } }

The advantage of using a full-featured framework such as Windsor is that there is no longer any code (not even in ApplicationStartUp) tied to particular implementations of dependencies. Objects that need dependencies can still ask the DependencyResolver class to retrieve them and the DependencyResolver will use whatever implementation of IDependencyResolver was provided to it to satisfy the request.

In the downloadable source code accompanying this article, you can see how I configure the Windsor container using Binsor, a great tool for configuring Windsor without XML files. Ayende Rahien developed Binsor and it allows me to configure dependencies using configurations stored outside of the application.

Wrapping Up
I have covered a lot of ground in this article. I started with a single-tiered application that breaks many of the best practices that many of you are already well aware of. I introduced a layered architecture and some refactoring techniques that drove me to use the dependency inversion principle. You can see the benefit of coding to abstractions vs. implementations. To top it off, I introduced the concepts of dependency injection and service locators as tools to help you realize the benefit of coding to abstractions.

You now have another arsenal of tips and techniques that you can start applying to realize more loosely coupled solutions in your application architectures.



Jean-Paul S. Boodhoo is a .NET delivery expert who has been working with the .NET Framework since beta 1 of .NET 1.0. He has over seven years of experience in architecting, designing, and developing applications. He is as an independent consultant who helps teams realize success through agile practices and pragmatic Behavior Driven Development (BDD) techniques. He has written articles for Visual Studio Magazine, DevX, and MSDN that use BDD to apply .NET pragmatically. Jean-Paul has presented on the popular podcast/screencast .NET Rocks! and DNRTV and has delivered Webcasts for Microsoft on the topic of design patterns in the real world. He is a member of the MSDN Canada Speakers Bureau and a Microsoft Most Valuable Professional (MVP). He makes continual efforts to update his blog.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap