Inversion of Control and Dependency Injection


By Erik Dörnenburg

Previous chapters discussed how domain objects and the corresponding infrastructure can be designed and developed. In this section, I want to present a particular design technique, or pattern, that can be used to assemble objects at runtime. The general principle is known as Inversion of Control and the particular pattern has been named Dependency Injection.

No Object Is an Island

Almost any object in our applications that provides business functionality depends on other objects. These can be objects that provide data or a service of some form. When we design and develop our objects using TDD, we usually explicitly provide the objects required, and often we use stubs or mocks in place of them to isolate the object under test. At runtime, however, our objects require a real implementation, and in one way or another all objects in our application must be instantiated and wired up.

The most common approach to resolving a dependency between one object and another is to place the responsibility for creating or acquiring the dependent object with the object that has the dependency. The simplest way to do this is to have the object that has the dependency instantiate the object it requires.

As an example, let us assume we are creating a pricer, an object that calculates the price for a financial instrument such as a bond. Of course, the pricer needs the bond that should be priced, but we decide that this is not part of the state of the pricer object and is to be passed in with each pricing request. Therefore, our class could look as follows:

public class BondPricer {     public double GetPrice(Bond bond) {         /* do something really complicated here */     } }


The price of the bond depends on other factors, though, and one example is something called a discount curve. (It does not matter whether you are familiar with the domain or not; for this example, the pricer could also depend on a wombat and nothing would be different.) A curve can be used to price many different bonds and remains relatively constant. We therefore decide that the curve should be part of the state of the pricer and keep it in a member variable which is initialized in the constructor.

//BondPricer private readonly DiscountCurve curve; public BondPricer() {     curve = new DiscountCurve(); }


Note that we could have created the curve on demand, but the discussion on when lazy instantiation is useful is a different one. In any case, the pricer now has a direct dependency on the curve. It is also likely that the discount curve object itself depends on other objects that it instantiates at an appropriate time. This looks like a viable approach, but upon closer inspection problems become visible: We have a functioning set of objects to price our bonds, but how flexible and maintainable is our solution?

Let us assume that the discount curve object reads the individual points of the curve from a database. This is what the users wanted. In a later iteration, we are asked to build a feature that allows the users to analyze what-if scenarios. For this the users want to specify the points for the curve in the UI. Nothing easier than that: We create an interface from the DiscountCurve class and provide two separate implementations, DatabaseDiscountCurve and InMemoryDiscountCurve. We change BondPricer class so that it only uses the methods on the interface and is thus no longer coupled to the database-based implementation.

Unfortunately, even though the calculation code in the bond pricer only depends on the discount curve interface, we must instantiate a concrete implementation of the IDiscountCurve interface at some point, the constructor of BondPricer in this example. At this point, we have to decide which of our two implementations we want to instantiate, but whichever one we choose, our class is coupled to the interface as well as to that implementation. The following diagram (Figure 10-9) shows the class design and clearly illustrates the problem: the creates link between the BondPricer and the DatabaseDiscountCurve.

Figure 10-9. Dependencies when direct instantiation is used


Another issue with direct instantiation is that there is no single place in the application to control the creation of discount curves, which makes it impossible to reuse the same instance for different purposes. This can be quite problematic, and if we consider that the original version of the curve loads information from a database, it becomes obvious that recreating new instances can have a severe impact on the performance of our application.

Before we discuss Inversion of Control and Dependency Injection, let us first look at another common approach to solving these issues.

Factories, Registries, and Service Locators

One way to address the coupling issue is through the use of the Factory pattern. We create a factory class that provides the required objects; in our case, an object that implements the discount curve interface.

public class DiscountCurveFactory {     public static IDiscountCurve GetCurve() {         return new DatabaseDiscountCurve();     } } public class BondPricer {     private readonly IDiscountCurve curve;     public BondPricer() {         curve = DiscountCurveFactory.GetCurve();     } }


With this factory, the pricer is no longer tightly coupled to a curve implementation, which is what we wanted to achieve. Unfortunately, in its place, the factory class itself is now tightly coupled to a specific IDiscountCurve implementation, so it remains difficult to use different curve implementations in different applications of the pricer. To overcome this problem we could create a factory interface and pass in different factory implementations, but this means that we would have to solve the problem of getting the right factory, which is similar to getting the right objects. In other words, we would add a level of indirection that provides additional flexibility but would just move the key problem we have.

What we have not shown in the code is that the Factory pattern also addresses the issue of instance management. Because we have a central place in our code that is responsible for providing curve instances, namely the GetCurve() method, we can choose to implement alternative policies that apply to the entire application. For example, rather than always creating a new curve, we could maintain a single instance or, if our application is multi-threaded, we could choose to manage a pool of instances.

In a further step toward complete decoupling, we can replace the factory with an implementation of the Registry pattern [Fowler PoEAA]. Much like a factory, a registry provides objects of a given type but rather than hard-coding the type of object, it externalizes this decision and instantiates the objects using reflection. This approach represents an improvement on the Factory pattern for our purposes because we have now separated two concerns: We have one class that is responsible for the provisioning of objects, the registry, and we can move the code that decides which concrete class to use in each case to other more suitable places in our code base.

The implementation of such a registry is simple but effective and finally achieves our goal of decoupling BondPricer from a concrete implementation of IDiscountCurve.

public class DiscountCurveRegistry {     private Type curveType;     public static void RegisterCurveType(Type aType) {         curveType = aType;     }     public static IDiscountCurve GetCurve() {         return (IDiscountCurve)Activator.CreateInstance(curveType);     } }


Figure 10-10 shows the resulting class design. It highlights that the pricer only depends on the interface, but it also draws our attention to two important implications of using a registry: The pricer depends on the registry class, and we need some other code, an assembler, that provides the concrete implementations to the registry.

Figure 10-10. Dependencies when a Registry is used


The curve objects we are using in this example are data sources, but it is easy to see that we can use the same patterns to locate services providing objects (for example, an audit service that writes the calculated price to a log). Used in this way, the pattern is often referred to as Service Locator [Alur/Crupi/Malks Core J2EE Patterns].

In implementation, most locators go one step further and do not provide individual sets of register and get methods for each service, but instead provide a single set of generic methods and a dynamic naming scheme.

public class Locator {     public static void RegisterType(String name, Type aType) { ... }     public static Object GetInstance(String name) { ... } }


Using a Service Locator, we register a concrete curve implementation with the locator under a name, most likely the name of the interface. With this setup, our bond pricer can now get hold of the discount curve by asking the locator for one.

public class BondPricer {     private readonly IDiscountCurve curve;     public BondPricer() {         curve = (IDiscountCurve)Locator.GetInstance("MyBank.IDiscountCurve");     } }


We have chosen to use strings in our naming scheme, which is a common practice, but object types would serve equally well. Using object types has the advantage of maintaining full support for automatic refactorings provided by our IDE and also helps with better error checking, though these benefits come at the expense of some flexibility. Whichever approach we choose, the pattern remains the same and provides the same degree of decoupling.

Constructor Dependency Injection

With registries and service locators, we have managed to decouple an object from the objects on which it depends by using an indirection, in this case a somewhat glorified lookup table. Setup code is responsible for deciding which concrete implementations to use, and the responsibility for finding the dependent objects lies with the object that has the dependencies. It uses the passive locator object and actively pulls the objects it requires from the locator whenever it deems necessary.

In contrast to that, Dependency Injection turns this process around and makes the object that has the dependencies passive. The object declares its dependencies but is otherwise completely oblivious as to where the dependencies come from. The process for resolving the dependencies and finding the dependent objects is left to an external mechanism, which may be implemented in a variety of ways. Usually, applications contain generic infrastructure code that loads a configuration file or uses an internal registry to keep a list of all objects and their dependencies. However, the Dependency Injection pattern is not concerned with this aspect. The infrastucture code is strictly complimentary, and it is even possible to satisfy the dependencies by passing in the objects from application code.

There are several flavors of dependency injection. Let us have a look at Constructor Dependency Injection first. Following this pattern, an object declares its dependencies in its constructor, and the code that creates the object ensures that valid instances are supplied at runtime.

public class BondPricer {     private readonly IDiscountCurve curve;       public BondPricer(IDiscountCurve aCurve) {         curve = aCurve;     } }


An important observation is that this code does not depend on anything but concepts related to the problem domain. It does not require a reference to a factory or registry, and it is not concerned with finding or creating the instances. As such, dependency injection promotes the design of elegant software, which usually exhibits a good separation between domain and infrastructure concerns.

Figure 10-11 shows the design using dependency injection. We have also included an assembler object that is responsible for creating the pricer and the curve on which the pricer depends. The diagram highlights the fact that with this design there is no dependency from any domain specific object to generic infrastructure code such as the assembler.

Figure 10-11. Dependencies when Dependency Injection is used


The fact that the bond pricer is not coupled to any infrastructure code also improves testability. Consider the first implementation of the pricer, which used the initial implementation of the discount curve that loads its points from the database. Tests of this class would always hit the database, and we know from previous chapters that this is a bad idea. With constructor injection, we can easily supply a stub or a mock implementation of each dependency, and so it is very straightforward to make the tests of the pricer independent of the database.

[Test] public void testPriceIncludesValuesForOutstandingCoupons () {     IMock curveMock = new DynamicMock(typeof(IDiscountCurve));     IDiscountCurve curve = curveMock.MockInstance as IDiscountCurve;     BondPricer pricer = new BondPricer(curve);     /* do actual test */ }


This approach is simpler than any solution that can be achieved with the other patterns we have discussed before. If we had opted for a design with a registry, for example, it would have been necessary for the test to create a registry and register the mock with it. With dependency injection, we simply pass in the required object. The underlying reason for this simplicity is the high level of decoupling provided by the Dependency Injection pattern, which makes it easy to use an object in multiple environments, at a minimum in the normal runtime environment and the testing environment.

We have not managed to replace all the functionality of the Registry pattern by introducing the Dependency Injection pattern. In the previous design, the registry did also provide another important service, namely a mapping between abstract types, the IDiscountCurve interface, and a concrete implementation to be used in a given application or context.

Surprisingly often, it is sufficient to hard-code the mapping in the code that creates the object. In our example, we may have two different methods that are invoked from different points in the user interface, one that prices the selected bond based on information from the database and another one that first asks the user for a few points, then constructs an in-memory curve from the points and finally creates a pricer based on that curve. In both cases, we can simply configure the pricer in the corresponding methods. As a matter of fact, using a registry in this example would feel awkward.

public class BondInfoForm : Form {     private TextBox priceTextBox;     private Bond SelectedBond { get {...} }     public void CalculateActualPrice() {         DatabaseDiscountCurve curve = new DatabaseDiscountCurve();         BondPricer pricer = new BondPricer(curve);         priceTextBox.Text = pricer.GetPrice(this.SelectedBond);     }     public void CalculateScenarioPrice() {         InMemoryDiscountCurve curve = new InMemoryDiscountCurve();         this.GetCurvePointsFromUser(curve);         BondPricer pricer = new BondPricer(curve);         priceTextBox.Text = pricer.GetPrice(this.SelectedBond);    } }


In many cases, simplicity is a trade off, and simple designs often fail to address more complex requirements. Fortunately, the simplicity in creating components designed according to the Dependency Injection pattern does not make it hard to create complex mapping and configuration schemes, and there are several lightweight IoC containers that can fulfill the same role as a registry. (I will discuss two of them later.) This means that we can choose between a simple hard-coded solution as shown previously and a more flexible mapping provided by some infrastructure code.

Another interesting consideration is an object that has optional dependencies. In the case of the bond pricer, the accuracy of certain calculations can be increased by taking local holidays into account, which means that a holiday calendar is an optional dependency. With constructor dependency injection, we express this by providing two constructors, one for each valid set of dependencies.

public class BondPricer {     private readonly IDiscountCurve curve;     private readonly IHolidayCalendar calendar;     public BondPricer(IDiscountCurve aCurve) {         curve = aCurve;     }     public BondPricer(IDiscountCurve aCurve         , IHolidayCalendar aCalendar) : this(aCurve) {         calendar = aCalendar;     } }


It would also be possible to use a constructor with all arguments and pass in null for the optional ones, but this makes the contract less explicit, especially when the number of dependencies increases. It is therefore advisable to always provide one constructor for each valid set of dependencies and, if possible, to chain the constructors. If this seems impossible, it is probable that the class in question has too many dependencies and should be broken up.

Setter Dependency Injection

As the aptly chosen name suggests, Setter Dependency Injection uses setters rather than the constructor to inject the dependencies. Consequently, the pricer has a default constructor but requires an additional property with a setter.

public class BondPricer {     private IDiscountCurve curve;     public BondPricer() {     }     public IDiscountCurve DiscountCurve {         set { curve = value; }     } }


Setter injection provides the same degree of decoupling as constructor injection, and it is easy to see that creating pricer objects in unit tests is as straightforward as with constructor injection. An important difference is that the pricer is in an invalid state until the setter method has been called, something that is not possible with constructor injection. Additionally, we cannot declare the curve variable as readonly because it is not initialized in the constructor and, finally, with setter injection it is a little more tricky and error-prone to implement final initialization functionality that needs to be run after all the dependencies have been injected.

Setter injection has its uses despite these shortcomings because it scales better to components with many dependencies. In a component with three optional dependencies and a strict view on constructor injection, we would need eight constructors. One could argue that the component needs refactoring, but this is not always practical, and it is good to have a pattern at hand for situations like this. In a similar vein, many existing objects have a default constructor and properties, and therefore we can only use them with IoC containers that support Setter Dependency Injection.

There are further styles of Setter Dependency Injection, such as Interface Driven Setter Dependency Injection, that use marker interfaces to distinguish setter methods that describe dependencies from other public setter methods. These styles are either not widely used or do not significantly change the key characteristics of setter injection, so I will not discuss them here.

Inversion of Control

Having discussed the Dependency Injection pattern and concrete implementations of it, let us now turn to Inversion of Control, also known as the Hollywood Principle ("Don't call us, we'll call you!"). Strictly speaking, IoC is not a pattern but a general principle that simply states that objects should rely on their environment to provide other objects rather than actively obtaining them.

Historically, the term IoC was frequently used to describe the approach certain containers took to create components at runtime. Looking at these containers, Martin Fowler raised an important question, namely "What aspect of control are they inverting?" He suggested that we recognize the setter- and constructor-based approaches implemented by these containers as a concrete pattern and use the more specific name Dependency Injection for it [Fowler Inversion-OfControl]. This also allowed people to distinguish between the Dependency Injection pattern and other types of IoC.

A widely used pattern that adheres to the IoC principle but is not a Dependency Injection variant is Contextualized Dependency Lookup. For our bond pricer, using this pattern would mean that we continue to use a registry-like class, which is often called context in this pattern, but let the container provide this context to our object, which is the inversion of control aspect. In code this can look as follows:

public interface IServiceContext {     public Object GetInstance(String name); } public interface IComponent {     public void Initialize(IServiceContext context); } public class BondPricer : IComponent {     private IDiscountCurve curve;     public void Initialize(IServiceContext context) {         curve = (IDiscountCurve)context              .GetInstance("MyBank.IDiscountCurve");     } }


On closer inspection, this pattern looks like a hybrid between the Service Locator and the Setter Dependency Injection patterns. This is no accident because historically this pattern is one of the first IoC patterns and proved to be an important stepping stone to the more elegant Dependency Injection patterns.

Dependency Injection with the Spring.NET Framework

The Spring.NET application framework [Spring.NET], which is a port of the widely used Java Spring framework [Spring], provides a very flexible IoC container that supports various flavors of Dependency Injection. This container is simply a component, actually an interface with several implementations, that is responsible for loading the configuration and setting up the objects accordingly. Historically, the term container was used to illustrate that an IoC container can be an alternative to a standard J2EE container on the Java platform.

Returning to the constructor injection example described earlier, let us examine how we would use the container provided by Spring.NET to get a configured instance of a pricer.

The preferred way to describe components and their dependencies with Spring.NET is with an XML configuration file. We declare our objects by type, including the assembly they are loaded from, and then resolve the dependency of the pricer on the discount curve by a nested constructor-arg element in the declaration of the pricer.

<objects>   <object name="DbCurve" type="MyBank.DatabaseDiscountCurve, MyBank"/>   <object name="DbPricer" type="MyBank.BondPricer, MyBank">     <constructor-arg><ref local="DbCurve"/></constructor-arg>   </object> </objects>


To access the objects in our code, we can create an instance of IApplicationContext, tell it to load the configuration from the config file, and then ask it for an object by name. The context ensures that all dependent objects are created and injected where required.

IApplicationContext context = new XmlApplicationContext("spring.xml"); BondPricer pricer = (BondPricer)context.GetObject("DbPricer");


In real applications, it is uncommon to use the application context so directly because it makes the code that creates the pricer dependent on the context. This is very similar to the dependency between our classes and the registry in the Registry pattern that we wanted to avoid. If we assume that the class that requires the pricer is a Windows form, we can use dependency injection for the form itself and add the form to the XML configuration. Now, when the form is created, the pricer is injected by the container and the form does not have to deal with the application context.

In this case, however, there must be some other piece of code that creates the form, and this code would look up the form from the context and would therefore be coupled to the context. Clearly, it cannot be turtles all the way down. To solve this issue most containers provide integration with the application frameworks, Spring.Web for example, such that the initial application-specific components, the Windows forms and Web pages, can simply use dependency injection while the application framework is responsible for creating them using the context. If there is no integration for a particular container/framework combination it is best to use dependency injection as much as possible and limit the dependency on the context to one class.

If we prefer to use setter injection, we can use the setter-based pricer implementation discussed before and change the Spring.NET configuration element for the pricer. The code that obtains the pricer remains unchanged because the container deals with creating the objects.

<objects>   <object name="DbCurve" type="MyBank.DatabaseDiscountCurve, MyBank" />   <object name="DbPricer" type="MyBank.BondPricer, MyBank" >     <property name="DiscountCurve">       <ref local="DbCurve"/>     </property>   </object> </objects>


The authors of Spring.NET generally advocate setter injection, but the container supports both styles equally well. As a matter of fact, nothing stops us from combining constructor and setter injection. If we design our pricer so that it declares it as required, or non-optional, dependency on the discount curve in the constructor and provides a settable property for the optional dependency on the holiday calendar, we can configure a pricer with a calendar as follows:

<objects>   <object name="DbCurve" type="MyBank.DatabaseDiscountCurve, MyBank" />   <object name="Calendar" type="MyBank.DefaultHolidayCalendar, MyBank" />   <object name="DbPricer" type="MyBank.BondPricer, MyBank" >     <constructor-arg><ref local="DbCurve"/></constructor-arg>     <property name="HolidayCalendar">       <ref local="Calendar"/>     </property>   </object> </objects>


This only scratches the surface of the possibilities that Spring.NET offers, but even these simple features can help enormously with configuring components that are designed according to the Dependency Injection pattern. One can even argue that placing too much and too complex logic into the configuration file provides flexibility that is rarely needed at the expense of maintainability.

Auto-Wiring with PicoContainer.NET

The configuration files used in the previous section were easy to understand and their purpose, namely to declare a few classes and the dependencies between them, was very explicit. However, they are relatively verbose, and they duplicate information that is also available via the .NET type system. For example, the fact that the pricer depends on a curve is expressed in the declaration of the constructor on the pricer class. In fact, all the information required in our example is available from the type system.

Most IoC containers support the resolution of dependencies based on runtime information. This feature is known as auto-wiring because the setting of dependent components, the wiring, occurs automatically, without any additional configuration. Spring.NET supports auto-wiring as an alternative to XML files, but I will use PicoContainer.NET [PicoContainer] in this section to add variety and because for this container, auto-wiring is the preferred option.

Using PicoContainer.NET, the only step that is required to enable the container to resolve dependencies and create objects is to register the concrete implementation classes with the container.

IMutablePicoContainer pc = new DefaultPicoContainer(); pc.RegisterComponentImplementation (typeof(DatabaseDiscountCurve)); pc.RegisterComponentImplementation ("pricer", typeof(BondPricer));


By placing the declarations into code, we remove redundancy and we get better support from the IDE because it is now able to detect misspelled type names, and it can also safely rename types in automated refactorings.

The objects are obtained from the container in almost the same way as they are obtained from the application context in Spring.NET, and for that reason the same considerations regarding a dependency on the container apply: Almost all classes should make use of dependency injection, leaving only one central class that interacts with the container to obtain the initial components.

BondPricer pricer = (BondPricer)container.GetComponentInstance("pricer");


The previous example also shows that, in addition to using the type system, PicoContainer.NET supports named components: the pricer in this example. This makes it possible to look up objects without having to specify their type, which introduces another degree of decoupling.

Optional dependencies are handled automatically because PicoContainer. NET uses the most complex constructor for which it can satisfy the dependencies. In the previous example, PicoContainer.NET uses the one argument constructor for BondPricer, which only takes a curve, but if we add the registration of the holiday calendar, PicoContainer uses the two argument constructor when we get the pricer object.

IMutablePicoContainer pc = new DefaultPicoContainer();   pc.RegisterComponentImplementation (typeof(DatabaseDiscountCurve)); pc.RegisterComponentImplementation(typeof(HolidayCalendar)); pc.RegisterComponentImplementation ("pricer", typeof(BondPricer));


As far as object dependencies are concerned, we are not losing any flexibility by using type-based auto-wiring instead of a configuration file-based approach, and at the same time we benefit from an improved development experience. Auto-wiring does not work equally well if a class expects parameters, such as hostnames, port numbers and timeouts, in their constructor because primitive types do not convey much information about their intended purpose. If we register two integers with the container, one that specifies a port number and the other one a timeout, the container sees both of them as integers and has no way of determining which one to use in a constructor that takes one integer.

The underlying question is whether the container should support the injection of required components in the same way as it handles the setting of configuration parameters or whether both requirements are seen as different concerns that should be handled with the most suitable approach for each concern. Using type-based auto-wiring arguably provides benefits over configuration files when managing dependencies, so one could use auto-wiring for this purpose. In the same scenario, configuration parameters can be set with setters for named properties, which is arguably more intuitive for that purpose.

A third IoC container that is available for the .NET platform is Windsor, which is part of the Castle project [Castle]. The core container implementation of the Castle project, which the developers call micro-kernel, uses type-based auto-wiring but it also allows plug-ins to add additional dependency resolution strategies. The Windsor container [Windsor], which is based on the micro-kernel, adds support for configuration files, thus allowing developers to choose the most appropriate mechanism. This is an approach that was successfully pioneered by NanoContainer [NanoContainer], a Java container that is layered on top of the Java version of PicoContainer.

Nested Containers

The fact that the container in Spring.NET is named ApplicationContext hints at the fact that the container defines a context for the objects that retrieve components from the container. These objects interact with the returned objects, and their behavior depends on the concrete implementations provided by the container; hence, the container can be seen as the provider of a context in which objects act. The name further suggests that the context created from the configuration file is application-wide, and all lookups in an application share the same context.

Web applications use multiple contexts, and most Web frameworks make them explicit in the form of application, session, and request objects. Request-handling code, such as the code behind a Web page, usually depends on several components. Some of the components are part of the application context because they can be shared by all request handlers while others are part of the individual request context because they should only be used by a single request.

Some IoC container implementations support this model and allow containers to be nested, which means that a container can have a parent container and, as a direct consequence, child containers. When a container cannot satisfy all dependencies required to create a component, it tries to obtain the missing components from its parent container, which in turn can request components from its own parent container. This set-up broadly resembles the Chain of Responsibility design pattern [GoF Design Patterns].

As an example, consider the creation of a request handler or page that has a dependency on a database connection and a logger. The database connection should only be used by one request at a time while the logger can be shared by all requests. This, however, is of no concern to the request handler.

public ResultPage : Page {     private Logger logger;     private IDbConnection connection;     public ResultPage(Logger aLogger, IDbConnection aConnection) : base() {         logger = aLogger;         connection = aConnection;     } }


In our application, a container is associated with the application. Additionally, each session has a child container, and for each request in a session, a child container of the corresponding session container is created. Our application maps a request URL to a page type, and it contains the following generic code that creates new request handlers for a given page type:

public RequestHandlerFactory {     IPicoContainer sessionContainer;     public Page CreatePage(Type pageType) {         IMutablePicoContainer reqContainer = CreateRequestContainer();         reqContainer.RegisterComponentImplementation(pageType);         return (Page)reqContainer.GetComponentInstance(pageType);     }     private IMutablePicoContainer CreateRequestContainer() {         IMutablePicoContainer pc = new DefaultPicoContainer(sessionContainer);         pc.RegisterComponentImplementation (typeof(SqlDbConnection));         /* register all other components here that should be available            to requests on a per request basis. */         return pc;     } }


After the request container is created in the CreatePage() method, the code registers the page type with the request container so that the page itself can use dependency injection. When it retrieves the actual page instance in the third line, the request container tries to create the page but, assuming a ResultPage is required, it cannot resolve the dependency on the Logger class because this has not been registered with the request container. In this situation, it will try to obtain the missing component from its parent, the session container, which also does not contain a logger and thus requests the logger from its parent, the application container. Provided that a logger is registered with that container, all dependencies for the page can be satisfied and it is created by the request container.

The advantage of this approach is that on the one hand components can be registered and shared at the appropriate level, while on the other hand objects that retrieve components from a container only have to deal with one container and can rely on it to find the required components in the chain of containers.

Service Locator Versus Dependency Injection

At this point we have discussed two different patterns that solve the coupling and instance management issues described at the beginning of this chapter. The question remaining is when to use which pattern.

It is safe to say that the Service Locator pattern is more commonly used today, but this is not necessarily because it is a better match for most applications. Instead, the most likely reason is that the Service Locator pattern has been around for a long time while Dependency Injection is a comparatively new pattern that is not as well known yet. In fact, developers arrived at the Dependency Injection pattern when they tried to improve the Service Locator pattern.

The complete decoupling of the components from the dependency resolution mechanism that is achieved by the Dependency Injection pattern improves testability because the tests do not have to set up specialized versions of a locator to provide stubs or mocked objects. It also helps with reuse in large systems as it avoids the problematic situation where different components to be used in a system are tied to different locator implementations.

Another advantage of the Dependency Injection pattern, especially with Constructor Dependency Injection, is that all dependencies of an object are easily visible in code. Modern IDEs are capable of highlighting all usages of a service locator in a given class (by pressing Ctrl-Shift-F7), but even with a sophisticated IDE this is not as easy as looking at a constructor.

It is often stated that Dependency Injection couples the life cycle of the object A that has the dependencies with the life cycles of the objects B and C it depends on, because B and C must be created before A so that they can be injected. This is not a problem with the Service Locator pattern because the creation of B and C can be postponed until they are requested from the locator by A. Fortunately, the same can be achieved, if not as naturally, with the Dependency Injection pattern, as one usage of the Component Adaptors in PicoContainer shows. Instead of injecting an instance of the actual class, the container injects a proxy that only creates the real instance when it is first used and from then on forwards all method calls to it. This is completely transparent to the object that has the dependencies, but it effectively decouples their lifecycles.

Summary

In this section, we looked at two issues that arise in the construction of any software system: the coupling of objects that depend on other objects and the managing of component instances. We first described how these issues can be addressed with the commonly used Factory, Registry, and Service Locator patterns. Realizing that these patterns have certain shortcomings, we discussed the Dependency Injection pattern, which addresses these issues in a novel way. This pattern, which is based on the Inversion of Control principle, provides better decoupling and scales well, from simple problems to very complex dependencies. It also improves testability, which is important for TDD, and allows domain models to be completely separated from infrastructure code, which makes it a useful tool for DDD.

As with almost any pattern, Dependency Injection is not a silver bullet and one cannot make the general recommendation to always replace the use of the Service Locator pattern with Dependency Injection. It is important to understand the differences and benefits of each pattern and use them appropriately. We have also seen that the Dependency Injection pattern has several flavors that provide trade-offs. Fortunately, choosing a certain flavor does not affect testability, which is important for development, and the containers that are used at runtime generally support all main variants. We can therefore choose, or even mix, different flavors as required.

The most important consideration when designing systems is to separate concerns, in this case configuration and usage of components, and then choose the most suitable design pattern from a rich catalogue.

Thanks, Erik!

I just want to stress once more what Erik ended with. You can think about Dependency Injection as what Martin Fowler talks about in his DSL article [Fowler LW] when he says that development can be thought of as creating good abstractions and then configuring them. Dependency Injection is one approach to taking care of part of the configuration.

I had to bite my tongue not to say "configuration aspect" in the previous sentence. You wouldn't have forgiven me for providing such a weak thread to the next subject, right? As a matter of fact, Dependency Injection and Aspect-Oriented Programming (AOP) actually often live in symbiosis, as you soon will find out.

Do you remember the discussions about Repository design? We can choose among several principles, such as

  1. Specific Repositories

  2. A base class

  3. A base class with generics

The problem with approaches 2 (on top of the lack of type safety, if you see that as a problem) and 3 is that they kind of create an automatic, unnatural abstraction that all Repositories will have. For example, not all Repositories should have delete possibilities.

The answer might be to create a smaller base class and write specific code for the variations. If you go that route, you might find that your base class will only have perhaps two or three methods (GetById(), MakePersistent(), and so on) and you get pretty close to approach 1 again.

You can try to lessen the code duplication by factoring out general code into helper classes that your specific Repositories delegate to. Then you'll have to write that boring and tedious delegation code, which won't make anyone happy.

Instead of starting with a base class that "never" has the right granularity, another approach is to build up the right Repository out of several tiny aspects, mixing in the right aspects to the right Repository. This is another way of thinking about the problem. It may not necessarily always be the right way (there is never one right way), but it is definitely worth more thought. I asked my friend Aleksandar Seovi to write a short introduction to


Applying Domain-Driven Design and Patterns(c) With Examples in C# and  .NET
Applying Domain-Driven Design and Patterns: With Examples in C# and .NET
ISBN: 0321268202
EAN: 2147483647
Year: 2006
Pages: 179
Authors: Jimmy Nilsson

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net