Search code examples
c#design-patternsdependency-injectionfactory-pattern

Why using the factory pattern when a simple dependecy injection is enough


Im looking to this example to understand the use of factory pattern.

I'm really amator in this field so excuse my silly question.

My problem is that i don't see the use of the factory pattern which return us the interface that we can inject it directly when we need to use it.

In the example above I would do something like this:

public class Program
{
    // register the interfaces with DI container in a separate config class (Unity in this case)
    private readonly IShippingStrategy _shippingStrategy;

    public Program(IShippingStrategy shippingStrategy)
    {
        _shippingStrategy= shippingStrategy;
    }

    public int DoTheWork(Order order)
    {
        // assign properties just as an example
        order.ShippingMethod = "Fedex";
        order.OrderTotal = 90;
        order.OrderWeight = 12;
        order.OrderZipCode = 98109;

        int shippingCost = _shippingStrategy.CalculateShippingCost(order);

        return shippingCost;
    }
}

Instead of injecting the factory :

public class Program
{
    // register the interfaces with DI container in a separate config class (Unity in this case)
    private readonly IShippingStrategyFactory _shippingStrategyFactory;

    public Program(IShippingStrategyFactory shippingStrategyFactory)
    {
        _shippingStrategyFactory = shippingStrategyFactory;
    }

    public int DoTheWork(Order order)
    {
        // assign properties just as an example
        order.ShippingMethod = "Fedex";
        order.OrderTotal = 90;
        order.OrderWeight = 12;
        order.OrderZipCode = 98109;

        IShippingStrategy shippingStrategy = _shippingStrategyFactory.GetShippingStrategy(order);
        int shippingCost = shippingStrategy.CalculateShippingCost(order);

        return shippingCost;
    }
}

Why taking the bruden to create a factory (thus adding an extra layer) when we can inject the interface directly to wherever we need to use it ?


Solution

  • I think you don't want just another article about the factory pattern but a short comprehensive answer. So, I'd like to focus on two things.

    More flexibility

    Most commonly, you'd set up your composition root where you basically say ...

    "if anyone wants IAnyService, he should get MyAnyServiceImplementation".

    This is fixed for your application. Once set up, your dependency injection container will serve the class instances you registered but you should not try to re-configure that container again. That's perfect for startup flexibility like registering implementation for data access components by the application's configuration, for example. Say ...

    "if anyone wants IUserRepository, he should get MsSqlUserRepository because we are working with MSSQL server".

    Of course, having that "immutable" composition root limits the possibilities to choose an implementation in runtime depending of the applications' state.

    Instead you can inject a class which decides on a current state which service implementation to choose. Data validation is a typical scenario for that pattern because there might be different rules for different entities on your system. The buzzword here is "rule pattern" or "strategy pattern".

    Lifetime control

    Think of a long-living class instance like a view (user interface) or any class attached to it (like a viewmodel or a controller). As long as a user is active on a view, the class is alive. By injecting a class instance to the constructor of the view controller, for example, you hold an active instance of it as long as the view lives.

    Let's say you want to use a data repository to connect to a database for example. These database access calls should be short and you do not want to keep connections open for a long time. With a repository factory, you could control the lifetime very precisely and make sure that the class is removed after it has been used:

    using (var repository = new _factory.CreateRepository(...))
    {
        return repository.GetAnything();
    }
    

    With this, a very lightweight class - the factory - gets injected and lives as long as the view controller lives. The heavy classes - the connection things - should not live long and are just created when needed.

    In fact, chances are that the repository is not instantiated at all if there's no requirement to load the data (because of an upfront cache hit, for example). If you would have injected the repository directly, you'd guarantee that one long living instance lives in memory in every case.