Example of Chain of Responsibility Adopted for Dependency Injection

Michael Bradvica joined Saxony Partners in 2020 as a Software Engineer on our app dev team. Previously, he has worked as a Full-Stack .NET Developer for technology services firms. You can read more of his thought leadership at MichaelBradvica.com – from which the blog post below has been adapted.

What is Chain of Responsibility?

The Chain of Responsibility is an original “Gang of Four” pattern that solves chained function calls. Its main purpose is to allow for an operation to occur without individual steps having any knowledge of any other step involved.

Examples of Chain of Responsibility

There are examples of Chain of Responsibility to be found online, but many of them fail to show how to properly integrate the pattern into your application. As a result, you end up with bloated classes that look something like this.

            public class BadChainCreationHandler : IRequestHandler<Widget>

            {

                private readonly IFirstDependency _firstDependency;

                private readonly ISecondDependency _secondDependency;

                private readonly IThirdDependency _thirdDependency;

                public BadChainCreationHandler(IFirstDependency firstDependency, ISecondDependency secondDependency, IThirdDependency thirdDependency)

                {

                    _firstDependency = firstDependency;

                    _secondDependency = secondDependency;

                    _thirdDependency = thirdDependency;

                }

                public async Task<Widget> Handle(Widget widget)

                {

                    var thirdHandler = new ThirdChainHandler(_thirdDependency, null);

                    var secondHandler = new SecondChainHandler(_secondDependency, thirdHandler);

                    var firstHandler = new FirstChainHandler(_firstDependency, secondHandler);

                    return await firstHandler.Handle(widget);

                }

            }

Let’s say that we have an application service handler that does something with a fictitious widget object. The handler is responsible for both the creation of the chain and its execution. In the spirit of Single-responsibility principle, the handler should not be aware of how the chain is created.

Our solution is quite simple: a factory!

            public interface IChainFactory<T>

            {

                IChainHandler<T> CreateChain();

            }

Our handler can now become…

            public class GoodChainCreationHandler : IRequestHandler<Widget>

            {

                private readonly IChainFactory<Widget> _chainFactory;

                public GoodChainCreationHandler(IChainFactory<Widget> chainFactory)

                {

                    _chainFactory = chainFactory;

                }

                public async Task<Widget> Handle(Widget widget)

                {

                    var chain = _chainFactory.CreateChain();

                    return await chain.Handle(widget);

                }

            }

Our factory is now responsible for the creation of our chain (and nothing else!).

            public class WidgetChainFactory : IChainFactory<Widget>

            {

                private readonly IFirstDependency _firstDependency;

                private readonly ISecondDependency _secondDependency;

                private readonly IThirdDependency _thirdDependency;

                public WidgetChainFactory(IFirstDependency firstDependency, ISecondDependency secondDependency, IThirdDependency thirdDependency)

                {

                    _firstDependency = firstDependency;

                    _secondDependency = secondDependency;

                    _thirdDependency = thirdDependency;

                }

                public IChainHandler<Widget> CreateChain()

                {

                    var thirdHandler = new ThirdChainHandler(_thirdDependency, null);

                    var secondHandler = new SecondChainHandler(_secondDependency, thirdHandler);

                    var firstHandler = new FirstChainHandler(_firstDependency, secondHandler);

                    return firstHandler;

                }

            }

Now, the handler is responsible only for starting the chain. This makes testing easier since you have reduced the number of dependencies in your handler. And, lastly, we have enforced consistency in the application by keeping the creation of objects inside our factories.

Making Digital Practical

Technology and digital strategy can be complicated, but our goal is simple: Make Digital Practical.

Saxony Partners will meet you where you are and help you get where you want to go. Our pragmatic approach helps ensure early success by leveraging proven technologies and practical solutions in a cost-effective way.

Our team of app dev experts, Full-Stack .NET developers, and software engineers work with clients to optimize their technology investments and leverage that technology to meet and solve business needs and challenges.

If you or your company is looking for help with software development, click here.