Login | Register   
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 2

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


advertisement
The Dependency Inversion Principle
As I mentioned, I stealthily slipped in the addition of this principle by introducing the passive view pattern—in particular, an interface (abstraction) for the view. The code for the View interface is as follows:

public interface IEmployeeView { event EventHandler Load; bool IsPostBack { get; } IList<IEmployee> Employees { set; } }

Pretty simple. Notice that the Load event is defined on the View interface, but you probably did not see an explicit implementation in the code-behind for the web page. This is because all web pages already define a Load event (with the same delegate signature), which satisfies the requirements of the interface. The same goes for the IsPostback property (already satisfied by the Page class). So why introduce an interface at all? The reason is that by coding to an interface (or an abstraction), the presenter can now talk to any object that implements the interface, whether it is a Windows Form, a mobile web control, or a web page. The presenter does not care about the actual implementation because it will always be talking to that implementation through an abstraction—the interface. This point demonstrates and introduces the importance of the dependency inversion principle, which can be described simply as:

"High-level components should not depend on low-level components; they should both depend on interfaces."
I will take that principle and utilize it to further clean up the code in the presenter.

I can now safely forget about the web page and its accompanying code-behind as it will remain static for the remainder of the refactoring process. Again, this speaks to the beauty of layered architecture and its ability to allow me to radically enhance the code without causing unnecessary ripple effects. In switching focus to the presenter, I am going to treat it as the "high level" component. What low-level components does it currently depend upon? The following code snippet should give you a couple of clues:

using (connection = new SqlConnection(ConnectionString)) { SqlCommand command = connection.CreateCommand(); command.CommandText = "SELECT * FROM Employees"; command.CommandType = CommandType.Text; connection.Open(); using (SqlDataReader reader = command.ExecuteReader( CommandBehavior.CloseConnection)) { DataTable results = new DataTable(); results.Load(reader); return results; } }

The preceding code couples the presenter to SqlConnection, SqlCommand, and SqlDataReader objects. In reality, worse than the coupling is the fact that this functionality does not really belong in the presentation layer of a project, because it still unnecessarily couples my presentation layer to the underlying physical database that is serving data to this application.

How can I address this problem? As the old computer-science axiom quotes, "Another layer of indirection will solve any problem." I am going to clean up the code in the presenter by adding a service layer to the project. For a detailed description of what a service layer is you can read this article. In the context of this article, all you need to focus on is that I am using the service layer as a façade to shield the presentation layer from the details of executing application functionality (in this scenario, retrieving a set of employees). I am going to utilize dependency inversion to come up with a contract (abstraction) for a service-layer component that will provide me the ability to "Get All Employees."

Examining the presenter closely, I can identify that its main role is to provide the View with a list of employees. It should not matter how that list is retrieved—that falls into the realm of the service layer (ultimately a mapper layer, but I'll omit talking about that in this article). With the main role identified, I come up with an interface for the service-layer component that the presenter will talk to:



public interface IEmployeeTask { IList<IEmployee> GetAllEmployees(); }

Yes, it's that simple. Again, following the dependency inversion principle leads me to believe that the presenter now has more than just a dependency on the View. Prior to the introduction of the service layer, the presenter was responsible for work that will (eventually) be pushed down into a service layer. The beauty of coding to abstractions is that the presenter has to talk only to a contract. At compile time the presenter does not know (or care) whether that contract is implemented. This allows me to complete the code for my presenter without the need for an actual concrete implementation of the service-layer component to even exist. Listing 3 shows the presenter refactored by introducing a dependency on a lower-layer component.

This cleans things up considerably. By utilizing the dependency inversion principle I have refactored the presenter into a much more highly cohesive unit. It is responsible for pushing data to the view (by means of an interface); and it retrieves the data from a service-layer component, that it also talks to using a contract (interface). In completing the refactoring for the presenter, I also quietly snuck in a new technique that makes utilizing dependency inversion all the more appealing: dependency injection.

Dependency Injection
If you take a closer look at Listing 3 you will see something very important happening in the constructor of the presenter:

public ViewEmployeesPresenter(IEmployeeView view, IEmployeeTask task)

Even though the presenter has its own cohesive set of responsibilities, it cannot accomplish its responsibilities without leaning on its dependencies. It just so happens that those dependencies are provided to it at the time of creation. This is a type of dependency injection called constructor-based dependency injection.

The main idea behind dependency injection is that if an object relies on other "components" to help it accomplish its work, it should not be responsible for creating those components; rather, the components it depends on should be injected into it in the form of abstractions.

The two most popular types of dependency injection are: constructor injection and setter injection.

In constructor injection, the object with dependencies is provided with all of its dependencies at the time of instantiation. It does not matter who creates it—it has to be given all that it needs to do its job. If it is not provided with all that it needs (read: null is passed in as a dependency), the object should not be expected to be able to complete its work. The one disadvantage to constructor-based injection is that it can get a little unwieldy if the object has a lot of dependencies that you have to provide to the constructor (of course, you could shield programmers from this by using a factory).

Setter injection uses a slightly different technique and almost always relies on a factory to create the object and wire it up with its dependencies. In setter injection there is no special constructor on the object with dependencies; instead, all its dependencies are injected by the factory through setter properties that the object exposes for the dependencies it needs. A disadvantage of setter-based dependency injection is that omitting the factory can unnecessarily couple components that use the object to the specific implementations of the dependencies the object requires.

The whole concept of dependency injection hinges around the concept of programming to abstractions. In this scenario the presenter has a dependency on:

  • A "view" that can render the information that is pushed to it.
  • A "service" that can execute application functionality on behalf of the presenter.
From a testability perspective, utilizing both dependency inversion and dependency injection allows for an increased level of testability as well as lower coupling between components of an application. The following code demonstrates how to utilize a combination of unit testing and mock objects to verify that when the presenter is constructed it subscribes to the Load event on the View interface:

[Test] public void ShouldSubscribeToViewEventsOnConstruction() { MockRepository mockery = new MockRepository(); IEmployeeView mockView = mockery.CreateMock<IEmployeeView>(); IEmployeeTask mockTask = mockery.CreateMock<IEmployeeTask>(); mockView.Load += delegate { }; LastCall.IgnoreArguments(); mockery.ReplayAll(); ViewEmployeesPresenter presenter = new ViewEmployeesPresenter(mockView, mockTask); mockery.VerifyAll(); }

The preceding code uses a mock object framework called Rhino Mocks and the NUnit unit testing framework. (As a practitioner of TDD, I would usually write this test first to drive out the functionality of the presenter; however, for this article I chose to not cloud the topic with another conversation about TDD!) You can find the accompanying code that makes this test pass in the presenter class itself:

public ViewEmployeesPresenter(IEmployeeView view, IEmployeeTask task) { this.view = view; this.task = task; HookupEventHandlersTo(view); } private void HookupEventHandlersTo(IEmployeeView view) { view.Load += delegate { LoadEmployees(); }; }

Notice that the constructor calls the HookupEventHandlersTo method, which in turn subscribes to the Load event defined by the View interface.

From a maintainability perspective I now have a guaranteed way of ensuring that whenever the presenter is constructed it subscribes to the Load event on the view. Of course, the actual functionality comes when the Load event on the view is triggered. This is where the presenter can communicate to the service layer and request a list of all employees, which it then pushes back to the view. Yet again, leveraging dependency injection allows me to easily write a test for this scenario. And again, I can substitute mock objects in place of "real" objects and the presenter is none the wiser because it is dependent on abstractions, not implementations.

The presenter has been relegated to nothing more than a component that acts on and reacts to its dependencies. The component that the presenter consumes seems to be the place where all the action should be happening. I'll drill down into the service layer and show how you can apply the techniques of dependency inversion and dependency injection to create a clean implementation of the IEmployeeTask interface.



Comment and Contribute

 

 

 

 

 


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

 

 

Sitemap