From Dependency Injection in .NET, Second Edition by Steven van Deursen and Mark Seemann

This article discusses why Abstract Factories shouldn’t be used to create stateful Dependencies with a short lifestyle and why it’s generally better not to use Abstract Factories to select Dependencies based on runtime data.


Save 37% on Dependency Injection in .NET, Second Edition. Just enter code fccseemann into the discount code box at checkout at manning.com.


When you start applying DI, one of the first difficulties you’re likely to encounter is when Abstractions depend on runtime values. For example, an online mapping site may offer to calculate a route between two locations, giving you a choice of how you want the route calculated: Do you want the shortest route? The fastest route based on known traffic patterns? The most scenic route?

The first response from many developers in such cases would be to use an Abstract Factory. Although Abstract Factories do have their place in software, when it comes to DI they are often overused. In many cases, better alternatives exist.

In this article we discuss one particular cases where better alternatives to Abstract Factories exist, which is why Abstract Factories should not be used to create stateful Dependencies with a short lifestyle.

When it comes to the abuse of Abstract Factories, a very common code smell is to see parameterless factory methods that have a Dependency as the return type, as the following listing shows.

Listing 1. Abstract Factory definition with parameterless Create method. [Code smell]

 public interface IProductRepositoryFactory
 {
     IProductRepository Create();                        
 }

  A parameterless factory method returning a new instance of a Dependency.

When an Abstract Factory has a parameterless Create method, it typically means that the creation of the Dependency isn’t depending on any runtime information.

Instead, Abstract Factories with parameterless Create methods are often used to allow consumers to control the lifetime of its Dependency. In the following listing, HomeController controls the lifetime of IProductRepository by requesting it from the factory, and disposing it when it finished using it.

Listing 2. HomeController manages the lifetime of its IProductRepository Dependency explicitly. [Code smell]

 
  
 public class HomeController : Controller
 {
     private readonly IProductRepositoryFactory factory;
  
     public HomeController(
         IProductRepositoryFactory factory)             
     {
         this.factory = factory;
     }
  
     public ViewResult Index()
     {
         using (var repository = this.factory.Create()) 
         {
             var products =
                 repository.GetFeaturedProducts();      
  
             return this.View(products);
         }                                              
     }
 }
 

❶   Abstract Factory is injected into the consumer

 Abstract Factory is used to create instance of the repository, whose lifetime must be managed explicitly.

The repository is used.

Since IProductRepository implements IDisposable, the created instance should be disposed of when the consumer is done. This makes IProductRepository a Leaky Abstraction as we’ll discuss shortly.

Disposing the repository is required when the used implementation holds on to resources, such as database connections, that should be closed in a deterministic fashion. Although an implementation might require deterministic cleanup, that doesn’t imply that it should be the responsibility of the consumer to ensure proper cleanup.

Parameterless factory methods are Leaky Abstractions

As useful as the Abstract Factory pattern can be, we must take care to apply it with discrimination. The Dependencies created by an Abstract Factory should conceptually require a runtime value, and the translation from a runtime value into an Abstraction should make sense. If you feel the urge to introduce an Abstract Factory because you have a specific implementation in mind, you may have a Leaky Abstraction at hand.

Definition

A Leaky Abstraction is an Abstraction that leaks through implementation details of the underlying implementation. Abstractions are meant to hide implementation details in order to manage complexity. From that sense a Leaky Abstraction is problematic.

Consumers that depend on IProductRepository shouldn’t care about which instance they get. At runtime we might need to create multiple instances, but where the consumer is concerned, there’s only one.

Important

Conceptually, there’s only one instance of a Service Abstraction. During the lifetime of a consumer, it shouldn’t be concerned with multiple instances of a Dependency. Failing to do this causes needless complication of the consumer, which means the Abstraction isn’t designed in favor of its consumers.

By specifying an IProductRepositoryFactory Abstraction with a parameterless Create method, we’re letting the consumer know that there are more instances of the given service and that it has to deal with this. Because another implementation of IProductRepository might not require multiple instances at all, we are leaking implementation details through the Abstract Factory with its parameterless Create method. In other words, we’ve created a Leaky Abstraction.

On a same note, Abstractions that implement IDisposable are Leaky Abstractions. Application code shouldn’t be responsible for the management of the lifetime of objects. Putting this responsibility inside the application code means we increase complexity of that particular class and make it more complicated to test and maintain. We’d often see this lifetime management logic be duplicated across the application, instead of being centralized in the Composition Root, which is what we’re aiming for.

DI is no excuse for writing applications with memory leaks, and we must be able to explicitly close connections and other resources as soon as possible. On the other hand, any Dependency may or may not represent out-of-process communication, and it would be a Leaky Abstraction if we were to model an Abstraction to include a Close method.

Some people resort to letting their Dependencies derive from IDisposable. The Dispose method is a Close method with another name, and that approach doesn’t solve the underlying problem.

This doesn’t mean that classes shouldn’t implement IDisposable. What this means is that Abstractions shouldn’t implement IDisposable. Because the client only knows about the Abstraction, it can’t be responsible for managing the lifetime of that instance. We elevate this responsibility back to the Composition Root.

Next, we’ll discuss how to prevent this code smell.

Refactoring toward a better solution

Consuming code shouldn’t be concerned with there being more than one IProductRepository instance. You should get rid of the IProductRepositoryFactory completely and let consumers depend solely on IProductRepository which should get injected using Constructor Injection. This advice is reflected in listing 2.

Listing 3 HomeController uses its Dependency directly without having to manage its lifetime. [Good code]

 
 public class HomeController : Controller
 {
     private readonly IProductRepository repository;
  
     public HomeController(IProductRepository repo)  
     {
         this.repository = repo;
     }
  
     public ViewResult Index()
     {
         var products =                              
             this.repository.GetFeaturedProducts();  
                                                     
         return this.View(products);                 
     }
 }
 

 Instead of injecting an Abstract Factory, IProductRepository itself is injected directly into the consuming HomeController.

 Instead of managing IProductRepository's lifetime by requesting it from an Abstract Factory and disposing it, HomeController merely uses it. IProductRepository doesn’t implement IDisposable anymore.

A common pattern to address this problem is the Proxy Pattern, an example of which is given in listing 4.

Listing 4. Proxy for IProductRepository which creates SqlProductRepository lazily [Good Code]

 
  
 public class SqlProductRepositoryProxy : IProductRepository
 {
     private readonly string connectionString;
  
     public SqlProductRepositoryProxy(string connectionString)
     {
         this.connectionString = connectionString;
     }
  
     public IEnumerable<Product> GetFeaturedProducts()
     {
         using (var repository = this.Create())         
         {
             return repository.GetFeaturedProducts();   
         }
     }
  
     private SqlProductRepository Create()
     {
         return new SqlProductRepository(               
             this.connectionString);
     }
 }
 

The Proxy creates and calls the SqlProductRepository internally only when its GetFeaturedProducts method is called. Such proxy should typically be part of the Composition Root to prevent the Control Freak anti-pattern.

The Proxy forwards the call to the real IProductRepository implementation.

The SqlProductRepository implementation still implements IDisposable. This way the Proxy can manage its lifetime.

Definition

The Proxy design pattern provides a surrogate or placeholder for another object to control access to it. It allows deferring the full cost of its creation and initialization until we actually need to use it. A Proxy implements the same interface as the object it is being surrogated for. It makes consumers believe they are talking to the real implementation.

Notice how the SqlProductRepositoryProxy internally contains factory-like behavior with its private Create method. This behavior, however, is encapsulated within the Proxy and doesn’t leak out, compared to the IProductRepositoryFactory Abstract Factory that exposes IProductRepository from its definition.

The SqlProductRepositoryProxy is tightly coupled to SqlProductRepository. This would be an implementation of the Control Freak anti-pattern if the SqlProductRepositoryProxy were defined in our Domain library. Instead, you should either define this Proxy in your Data Access library that contains SqlProductRepository or—more likely, the Composition Root.

The Composition Root is an especially well-suited location to place this Proxy class, since the Create method actually composes part of the object graph.

That’s all for this article.


If you’re interested in learning more about the book, check it out on liveBook here.