C# and FP – episode 3 – OOP today, Inversion of Control and Dependency injection.

Introduction

The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

Joe Armstrong, father of Erlang

He is right, you know! I totally agree with the quote above. The abuse of inheritance, leads to over complex systems with objects that have no clear responsibility and in general the SOLID principles go out of the window.

Inheritance goal it to build a classes hierarchy, arranging any notions from the generalized to specialized ones. The behavior of a class is defined in its interface. When a sub class inherits from a super class, it implicitly accepts the super class behavior.

This leads to a subclass that is strongly coupled to its super class, than it would be if it just used an instance of the super class as a component instead of inheriting from it.

Inheritance is making properties and methods of a class available for reuse and extension in sub classes. Mechanically, the subclass will inherit the implementation of the super class and thus also its interface.

But why is this happening?

Semantics, they always have been there

A key point that a lot of programmers don’t pay attention to, is the semantics. For example, a Stack class, might have a bloated interface. The Stack class public interface might not be just push and pop, as one would expect from a class named Stack, but it can also includes Get, Set, Add, Remove, Clear methods, probably inherited from List-ish class, totally inappropriate for a Stack.

The easy way out, is to override all of the unwanted methods, or/and adapt some like Clear, but that’s a lot of work to cover up a modeling mistake. Three modeling mistakes, actually, one semantic, one mechanical, one both:

  1. Semantically, the statement “a Stack is an List” is not true, Stack is not a subtype of List. A stack is supposed to enforce LIFO (Last In First Out), a constraint satisfied by the push/pop interface, but not enforced by List’s interface.
  2. Mechanically, inheriting from a List class, violates encapsulation; using a List class as a super, to hold the stack’s object collection is an implementation choice that should be hidden.
  3. Implementing a Stack by inheriting from a List class, is a cross-domain relationship: A List object, is a randomly-accessible Collection; Stack is a queuing concept, with specifically restricted (non-random) access. These are different modeling domains.

Inversion of Control, Dependency injection and objects (im)mutability

Inversion of Control

The Inversion of Control (IoC) term was first used by Johnson and Foote’s paper Designing Reusable Classes, published by the Journal of Object-Oriented Programming in 1988 [3]. It was later popularized by Robert C. Martin and Martin Fowler.

A synonym of IoC, that a lot people know and use, is the Hollywood Principle. This synonym comes by Richard Sweet on Mesa in 1983. In a list of design goals he writes: “Don’t call us, we’ll call you (Hollywood’s Law): A tool should arrange for Tajo to notify it when the user wishes to communicate some event to the tool, rather than adopt an ‘ask the user for a command and execute it’ model.” [3].

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user’s application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application [3].

Ralph Johnson and Brian Foote

Inversion of Control is what makes a framework different to a library. A library is essentially a set of functions that you can call. Each call does some work and returns control to the client. A framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by sub classing or by plugging in your own classes. The framework’s code then calls your code at these points [3].

Martin Fowler

The IoC principle is where an object’s initial condition and life cycle are handled by its environment rather than by the object itself. Thus if a Foo uses a BarHandler, the appropriate instance of the BarHandler will be provided by a container rather than obtained by the object.

Dan North

The benefits of IoC is that a class A is not dependent any more to a concrete implementation of a class B. So if class A in not dependent anymore to a concrete implementation of class B, to what is dependent now? To an interface, that class B, or B’ or B” implements!

Therefore when we change class B, is less likely to change class A that relies on the implementation of B. Another benefit is code isolation when we write unit tests. During testing, we will be able to inject mocks instead of concrete objects, and thus the unit testing process becomes possible and probably painless. I would like to add that mocking is considered by many programmers as a code smell. But this is for another day.

Inversion of Control can be implemented in various ways.

  1. Service locator pattern
  2. Dependency injection pattern
  3. Contextualized dependency lookup
  4. Template method design pattern
  5. Strategy design pattern

The two most famous techniques are the Service locator and Dependency injection patterns. But today we will focus on DI.

Dependency Injection

Dependency injection is a way to solve problems, like any other technique and pattern. But before using it, we have to make sure if we can minimize the dependencies in our code base. Don’t try to use DI in order to hide the problem under the carpet. The patterns are tools, not the goal. Never use a pattern for the sake of using a pattern.

There are two ways to implement DI.

  1. The Poor Man’s DI (or Pure DI as it is described by Mark Seemann [1, 2]), that is fully implemented by the programmer herself,
  2. By using DI frameworks like Ninject, AutoFac, Microsoft Unity, Castle. Although with DI frameworks there is a catch: DI frameworks with DI containers is the fact that they combine mutable objects and reflection to inject anything, which goes against a clean architecture!

I suggest you reading a great article about DI, by Mark Seemann, here. Pieces of it are used here.

Pure DI vs DI with a with a framework

usefulness vs. sophistication

First of all, Pure DI doesn’t’ come cheap. Never! It has a maintenance cost that becomes higher and higher, as the code base grows. It’s not panacea and doesn’t solve any problems you might face! That fact that is simple, makes it appealing on one hand, but needs quite a lot of patience on the other.

Another issue is that it’s strongly typed. Every time you refactor a constructor, you will break the Composition Root. If you are sharing libraries between more than one application, you may have more than one Composition Root to maintain [4].

The following example is a not that trivial example that Mark Seemann uses in his book. I like it cause it provides a lot of insights. You can find the example here.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ploeh.Samples.Booking.Persistence.FileSystem;
using System.IO;
using Ploeh.Samples.Booking.PersistenceModel;
using System.Threading;
using Ploeh.Samples.Booking.JsonAntiCorruption;
using Ploeh.Samples.Booking.DomainModel;
using System.Reactive.Subjects;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Ploeh.Samples.Booking.WebModel;

namespace Ploeh.Samples.Booking.Daemon
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueDirectory = 
                new DirectoryInfo(@"..\..\..\BookingWebUI\Queue").CreateIfAbsent();
            var singleSourceOfTruthDirectory = 
                new DirectoryInfo(@"..\..\..\BookingWebUI\SSoT").CreateIfAbsent();
            var viewStoreDirectory = 
                new DirectoryInfo(@"..\..\..\BookingWebUI\ViewStore").CreateIfAbsent();

            var extension = "txt";

            var fileDateStore = new FileDateStore(
                singleSourceOfTruthDirectory,
                extension);

            var quickenings = new IQuickening[]
            {
                new RequestReservationCommand.Quickening(),
                new ReservationAcceptedEvent.Quickening(),
                new ReservationRejectedEvent.Quickening(),
                new CapacityReservedEvent.Quickening(),
                new SoldOutEvent.Quickening()
            };

            var disposable = new CompositeDisposable();
            var messageDispatcher = new Subject<object>();
            disposable.Add(
                messageDispatcher.Subscribe(
                    new Dispatcher<RequestReservationCommand>(
                        new CapacityGate(
                            new JsonCapacityRepository(
                                fileDateStore,
                                fileDateStore,
                                quickenings),
                            new JsonChannel<ReservationAcceptedEvent>(
                                new FileQueueWriter<ReservationAcceptedEvent>(
                                    queueDirectory,
                                    extension)),
                            new JsonChannel<ReservationRejectedEvent>(
                                new FileQueueWriter<ReservationRejectedEvent>(
                                    queueDirectory,
                                    extension)),
                            new JsonChannel<SoldOutEvent>(
                                new FileQueueWriter<SoldOutEvent>(
                                    queueDirectory,
                                    extension))))));
            disposable.Add(
                messageDispatcher.Subscribe(
                    new Dispatcher<SoldOutEvent>(
                        new MonthViewUpdater(
                            new FileMonthViewStore(
                                viewStoreDirectory,
                                extension)))));

            var q = new QueueConsumer(
                new FileQueue(
                    queueDirectory,
                    extension),
                new JsonStreamObserver(
                    quickenings,
                    messageDispatcher));

            RunUntilStopped(q);
        }

        #region Console stuff
        private static void RunUntilStopped(QueueConsumer q)
        {
            var tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;

            var task = Task.Factory.StartNew(() =>
            {
                while (!token.IsCancellationRequested)
                {
                    q.ConsumeAll();
                    Thread.Sleep(500);
                }
            }, tokenSource.Token);

            Console.WriteLine("Type \"quit\" or \"exit\" to exit.");
            do
            {
                Console.Write("> ");
            } while (DoNotExit());

            tokenSource.Cancel();
        }

        private static bool DoNotExit()
        {
            var line = Console.ReadLine().ToUpperInvariant();
            return line != "QUIT"
                && line != "EXIT";
        }
        #endregion
    }
}

A lot of code indeed. This snippet, it’s a big object graph, with some shared sub graphs, and since it uses the new keyword to create all the objects, every time you change a constructor signature, you’ll need to update this code [4].

The technical that can be generated in the future can be huge. Please take under consideration any cyclomatic complexity that might exist in the Composition Root.

Dependency Injection with a framework

DI containers carry a number of prerequisites to behave as expected.

  • Abstractions: A DI container to matter, the application must have abstractions. Do not forget, that the goal is to keep loose coupling in our code base.
  • Clear and single responsibilities: Any abstraction should provide a single responsibility. Abstractions are meant to be reusable, so the container will expect all the implementations to follow the abstraction’s contract.
  • Correct architecture: The objects graph has to be well defined, and in way that will allow the container to work smoothly without any unexpected behaviors and exceptions. The objects must expose specific characteristics and messages exchange among them (information flow) must be clear.

Also, the containers must support with an easy and flexible way, the following DI lifecycle:

  • Register: The container must know which dependency to instantiate when it encounters a particular type. This process is called registration.
  • Resolution: When using an IoC container, no objects are created manually. The container does it for us. This is called resolution. The container must include some methods to resolve the specified type; the container creates an object of the specified type, injects the required dependencies, if any, and returns the object.
  • Dispose: The container manages the lifetime of the dependent objects. Most IoC containers include different lifetime managers to manage an object’s lifecycle and dispose it.

When you use DI containers, please pay attention to two following things:

  1. You can’t see how a DI container works. It relies on reflection (too) much, and you might feel that I don’t control my source code any more bad the container does.
  2. All classes have to be mutable, in order to able to receive injections, besides constructor injection.

A small example of a DI container (Ninject in this case) is the following:

// Write your code so it's flexible...
public class Samurai {
    public IWeapon Weapon { get; private set; }
    public Samurai(IWeapon weapon) 
    {
        this.Weapon = weapon;
    }
}

//...and let Ninject glue it together for you.

public class WarriorModule : NinjectModule
{
    public override void Load() 
    {
        this.Bind<IWeapon>().To<Sword>();
    }
}

The example was taken from http://www.ninject.org

Conclusions

Before choosing any path, calculate the values and costs related to it. If you start from zero, it’s better to start with Poor Man’s DI, and as soon you get to the point that gets to much to do it manual, consider shifting to a DI Container.

The next episode

In the next episode, we discuss about Language Integrated Query (LINQ).

[3]. https://martinfowler.com/bliki/InversionOfControl.html

[4]. https://blog.ploeh.dk/2012/11/06/WhentouseaDIContainer/

Write a Comment

Your email address will not be published. Required fields are marked *