👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Control Freak Antipattern in Dependency Injection

Control Freak Antipattern in Dependency Injection

Author - Abdul Rahman (Bhai)

Dependency Injection

7 Articles

Improve

Table of Contents

  1. What we gonna do?
  2. Why we gonna do?
  3. How we gonna do?
  4. Summary

What we gonna do?

When a class insists on creating its own dependencies instead of receiving them from outside, it becomes a Control Freak. In this article, let's learn about the Control Freak anti-pattern in Dependency Injection and understand why it violates the principles of loose coupling and Inversion of Control.

Why we gonna do?

The Control Freak anti-pattern is the exact opposite of Inversion of Control. When a class creates its own dependencies using the new keyword, factory methods, or service locators, it takes control over the dependency creation process instead of allowing external configuration.

This anti-pattern makes code tightly coupled, difficult to test, and hard to extend. It prevents you from swapping implementations, makes unit testing nearly impossible without modifying the class, and violates the Open/Closed Principle.

Common Manifestations of Control Freak

The Control Freak anti-pattern appears in several forms:

  • Newing up dependencies - Direct instantiation using the new keyword
  • Factory-based creation - Using concrete or abstract factories within the class
  • Overloaded constructors - Providing parameterless constructors that create default dependencies

How we gonna do?

Example 1: Control Freak Through Newing Up Dependencies

Here's a classic example of the Control Freak anti-pattern where a ProductService creates its own repository:


public class ProductService
{
    private readonly IProductRepository repository;
    
    public ProductService()
    {
        // Control Freak: Creating dependency directly
        this.repository = new SqlProductRepository();
    }
    
    public Product GetFeaturedProduct()
    {
        return repository.GetFeaturedProduct();
    }
}
            

Problems with this approach:

  • Tightly coupled to SqlProductRepository
  • Cannot be unit tested with a test double
  • Cannot switch to a different repository implementation
  • Violates the Dependency Inversion Principle

Example 2: Control Freak Through Concrete Factory

Sometimes developers try to solve the coupling issue by introducing a factory, but still maintain control:


public class ProductService
{
    private readonly IProductRepository repository;
    
    public ProductService()
    {
        // Still Control Freak: Using concrete factory
        var factory = new ProductRepositoryFactory();
        this.repository = factory.Create();
    }
    
    public Product GetFeaturedProduct()
    {
        return repository.GetFeaturedProduct();
    }
}

public class ProductRepositoryFactory
{
    public IProductRepository Create()
    {
        return new SqlProductRepository();
    }
}
            

This approach still exhibits Control Freak behavior because the class decides which factory to use and when to call it.

Example 3: Control Freak Through Abstract Factory

Even using an abstract factory can be a form of Control Freak if the class controls the factory selection:


public abstract class ProductRepositoryFactory
{
    public abstract IProductRepository Create();
}

public class ProductService
{
    private readonly IProductRepository repository;
    
    public ProductService()
    {
        // Control Freak: Deciding which factory to use
        ProductRepositoryFactory factory;
        
        if (ConfigurationManager.AppSettings["DatabaseType"] == "SqlServer")
            factory = new SqlProductRepositoryFactory();
        else
            factory = new AzureProductRepositoryFactory();
            
        this.repository = factory.Create();
    }
}
            

Example 4: Control Freak Through Overloaded Constructors

Another common manifestation is providing a parameterless constructor that creates default dependencies:


public class ProductService
{
    private readonly IProductRepository repository;
    
    // Control Freak: Parameterless constructor creates default dependency
    public ProductService() : this(new SqlProductRepository())
    {
    }
    
    // Proper constructor for DI
    public ProductService(IProductRepository repository)
    {
        this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }
    
    public Product GetFeaturedProduct()
    {
        return repository.GetFeaturedProduct();
    }
}
            

While this seems to offer flexibility, the parameterless constructor represents a Foreign Default because it creates a dependency defined in a different module.

Refactoring Away from Control Freak

The solution is to apply proper Constructor Injection and let the Composition Root or Dependency Injection Container handle dependency creation:


// Properly designed ProductService
public class ProductService
{
    private readonly IProductRepository repository;
    
    public ProductService(IProductRepository repository)
    {
        this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }
    
    public Product GetFeaturedProduct()
    {
        return repository.GetFeaturedProduct();
    }
}

// Composition Root handles dependency creation
public class CompositionRoot
{
    public ProductService CreateProductService()
    {
        IProductRepository repository = new SqlProductRepository();
        return new ProductService(repository);
    }
}
            

Benefits of Proper Dependency Injection

By eliminating the Control Freak anti-pattern, you gain:

  • Testability - Easy to inject test doubles
  • Flexibility - Can swap implementations without changing the class
  • Maintainability - Dependencies are explicit and clear
  • Single Responsibility - Class focuses on its core logic, not dependency management

Testing the Refactored Code

With proper dependency injection, unit testing becomes straightforward:


[Test]
public void GetFeaturedProduct_ReturnsExpectedProduct()
{
    // Arrange
    var expectedProduct = new Product { Id = 1, Name = "Test Product" };
    var mockRepository = new Mock<IProductRepository>();
    mockRepository.Setup(r => r.GetFeaturedProduct()).Returns(expectedProduct);
    
    var productService = new ProductService(mockRepository.Object);
    
    // Act
    var result = productService.GetFeaturedProduct();
    
    // Assert
    Assert.AreEqual(expectedProduct, result);
}
            

Summary

The Control Freak anti-pattern occurs when a class takes control of creating its own dependencies instead of receiving them from external sources. This violates the principles of Inversion of Control and leads to tightly coupled, hard-to-test code.

Whether through direct instantiation, factory methods, or parameterless constructors, Control Freak behavior makes code rigid and difficult to maintain. The solution is to embrace Constructor Injection and delegate dependency creation to the Composition Root, resulting in more flexible, testable, and maintainable code.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • Dependency Injection
  • Control Freak
  • Antipattern
  • DI
  • Dependency Creation
  • Constructor Injection