2

The fifth concept in the SOLID principle is the dependency inversion principle. It is heavily related to dependency injection, and Inversion of control, and when any of these concepts are mentioned, they are often used in a way that makes them synonymous with each other. Example:

Autofac is an addictive Inversion of Control container for .NET Core, ASP.NET Core, .NET 4.5.1+, Universal Windows apps, and more.

and

the Spring Framework implementation of the Inversion of Control (IoC) principle. IoC is also known as dependency injection (DI).

I am defining automatic factories here as factories that construct objects based on configuration. Autofac and Spring are two examples. Technically the are designed as IoC Containers, but broadly speaking they are used as automatic factories.

I often hear people calling Autofac, or Spring IoC "Dependency Injection" frameworks. Example:

We can call this as our IoC (Inversion of Control) container or a DI (Dependency Injection) framework.

So much of the online documentation seems to bring along with it the baggage of automatic factories. On the face of it, the SOLID principle does not seem to formally require automatic factories for constructing objects. But, is it a consequence of dependency inversion that automatic factories are required to correctly implement the SOLID principle? If the highest level of the app constructs the dependencies and then passes them in to the constructor of the highest level class, is this following the SOLID principle? Or, are IoC Containers required for this?

Take this C# Example

    public interface IMessageSender
    {
        void SendMessage(string message);
    }

    public class ConsoleMessageSender : IMessageSender
    {
        public void SendMessage(string message)
        {
            Console.WriteLine(message);
        }
    }

    public interface IApp
    {
        void SendHelloMessage();
    }

    public class App : IApp
    {
        private IMessageSender _messageSender;

        public App(IMessageSender messageSender)
        {
            _messageSender = messageSender;
        }

        public void SendHelloMessage()
        {
            _messageSender.SendMessage("Hello World!");
        }
    }

Here is Version A) without IoC / Automatic Factories:

    class Program
    {
        static void Main(string[] args)
        {
            var app = new App(new ConsoleMessageSender());
            app.SendHelloMessage();
        }
    }

Here is Version B) with Autofac for the IoC container

    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<ConsoleMessageSender>().As<IMessageSender>();
            builder.RegisterType<App>().As<IApp>();
            var container = builder.Build(ContainerBuildOptions.None);
            var app = container.Resolve<IApp>();
            app.SendHelloMessage();
        }
    }

Is Version B) more correct from a SOLID point of view? Would Version A) fail to conform to the SOLID principle because of the lack of an IoC container?

Note: I'm looking for a clear definition here. IoC containers are often bundled up with the concept of DI and SOLID, but the question is about whether or not they are strictly required. There is a question of whether or not they are accoutrement that have been added to SOLID over time

1

2 Answers 2

10

Is it a consequence of dependency inversion that automatic factories are required to correctly implement the SOLID principle?

No.

IoC Containers are merely a convenient way to instantiate objects. They're not a mandate for SOLID. You can instantiate the same objects without a container and still be SOLID.

If the highest level of the app constructs the dependencies and then passes them in to the constructor of the highest level class, is this following the SOLID principle?

Essentially, yes.

Your aggregate root (which could simply be your main method, if it's a console application) is responsible for standing up the object graph that makes your program (or aggregate) work.

Is Version B) more correct from a SOLID point of view?

No.

SOLID principles are based on classes and class hierarchies, not containers. Containers are one way to stand up an object graph, but they're not the only way. SOLID doesn't depend on containers.

Would Version A) fail to conform to the SOLID principle because of the lack of an IoC container?

No.

9
  • 1
    I like your answer but how can I validate it? Aug 17, 2019 at 3:37
  • 3
    Thought experiment: You have three classes that meet the SOLID principles. You use the new keyword to stand up an object graph using those three classes. The object graph is still SOLID. Now you use an IoC container to stand up that same object graph instead. Is the object graph still SOLID? Aug 17, 2019 at 3:46
  • I would have thought so but judging by your wording, I'm guessing not? Care to explicate why not? Aug 17, 2019 at 3:49
  • 3
    Yes, the object graph is still SOLID. It doesn't matter how you create your objects. You can create them with the new keyword. You can create them with Activator.CreateInstance. You can create them via carrier pigeon. They will still be the same objects when you are done. Therefore they will still have the same SOLID characteristics. Aug 17, 2019 at 3:51
  • While I think you're right, I still don't understand how the thought experiment validates the answer. Aug 17, 2019 at 4:42
1

I feel your pain on the confusion of Dependency Injection and Dependency Inversion. I've been on many job interviews and having to explain the difference.

I wouldn't say IoC containers are required, but they do make the instantiation of big dependency trees much easier. But using or not using IoC container has no bearing on if the code follows SOLID. In ideal codebase, code should have no knowledge about IoC container it resides in, if any.

Dependency Inversion often leads to many classes that need other classes that need other classes, with some classes being used by many other classes and often with different lifetimes. Your example doesn't do this problem justice, as you have only two classes. Instead, imagine an example with hundred of classes, even more interfaces (as class can expose multiple interfaces) and complex non-cyclical graph of dependencies between them. In this kind of example, the manual instantiation code becomes much more complicated and confusing than just IoC's combination of registration and resolution.

1
  • 1
    Thanks for acknowledging the confusion - especially around job interviews. These questions often get asked, but half the time, the interviewer wouldn't even know the difference between DI, and IoC. That's the spirit in which I asked the question. The example isn't supposed to elucidate any issues around instantiation. It's just supposed to show the distinction between IoC containers and non IoC containers. Aug 17, 2019 at 5:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.