Func vs Interfaces for IoC – Using Func for Inversion of Control

Architecturedelegatesinterfacesinversion-of-control

Context: I am using C#

I designed a class, and in order to isolate it, and make unit testing easier, I am passing in all its dependencies; it does no object instantiation internally. However, instead of referencing interfaces to get the data it needs, I have it referencing general purpose Funcs returning the data/behavior it requires. When I inject its dependencies, I can just do so with lambda expressions.

To me, this seems like a better approach because I do not have to do any tedious mocking during unit testing. Also, if the surrounding implementation has fundamental changes, I will only need change the factory class; no changes in the class containing the logic will be needed.

However, I have never seen IoC done this way before, which makes me think there are some potential pitfalls I may be missing. The only one I can think of is minor incompatibility with earlier version of C# which don't define Func, and this is not an issue in my case.

Are there any problems with using generic delegates/higher order functions such as Func for IoC, as opposed to more specific interfaces?

Best Answer

If an interface does contain only one function, not more, and there is no compelling reason to introduce two names (the interface name and the function name inside the interface), using a Func instead can avoid unnecessary boilerplate code and is in most cases preferable - just like when you start to design a DTO and recognize it needs only one member attribute.

I guess lots of people are more used to using interfaces, because at the time when dependency injection and IoC were getting popular, there was no real equivalent to the Func class in Java or C++ (I am not even sure if Func was available at that time in C#). So lots of tutorials, examples or textbooks still prefer the interface form, even if using Func would be more elegant.

You might look into my former answer about the interface segregation principle and Ralf Westphal's Flow Design approach. This paradigma implements DI with Func parameters exclusively, for exactly the same reason you mentioned already by yourself (and some more). So as you see, your idea is not a really new one, quite the opposite.

And yes, I used this approach by myself, for production code, for programs which needed to process data in form of a pipeline with several intermediate steps, including unit tests for each of the steps. So from that I can give you first hand experience that it can work very well.

Related Topic