Skip to content

Instantly share code, notes, and snippets.

@jehugaleahsa
Last active March 8, 2016 01:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jehugaleahsa/036952422c6f5739684e to your computer and use it in GitHub Desktop.
Save jehugaleahsa/036952422c6f5739684e to your computer and use it in GitHub Desktop.
Factories and the Rebirth of Object-Oriented Programming

As familiar as I am with OOP and design patterns, I rarely see myself coding in an object-oriented fashion. I make heavy use of the strategy pattern to swap out implementations based on configuration settings, but that's about it. Like most OOP enthusiasts, at one time I used patterns pretty heavily. Some composite pattern here, decorator there -- sprinkle in a little bridge pattern for taste. With a gradual introduction to TDD and functional programming, that's slowly faded away over the years.

In 2012, it dawned on me how freaking awesome functional programming is. With a few tricks here and there, you can almost completely eliminate the need for stateful classes. Instead, classes just get passed their dependencies via dependency injection (DI). Any other information is passed via method parameters. Closures and other functional hacks handled those edge cases where there seemed to be a need for state. This style of programming is especially effective when you start working with RESTful APIs or heavily threaded applications. Or at least I thought so...

A few weeks ago, it occurred to me how often I was seeing the keyword as in my C# code. After reviewing the code, I recognized that I was missing an obvious opportunity for polymorphism. Unfortunately, I've generally considered class hierarchies heavy-weight and hard to test. They require a lot of mental power to digest when you're wading through code. Template method is goddamn hard to test, too, believe me -- lining up mock objects to cause protected methods to fire in derived classes without duplicating test code -- ack!!!

Once you start using constructor injection, it is tempting to start passing everything you need to the constructor. Consider a simple example where you want a class to represent dice, called Die. Let's say you have a service IRandomNumberService that you use to generate random numbers. You also need the ability to create die with different numbers of sides. This might be the first go:

public class Die
{
    private readonly IRandomNumberService generator;
    private readonly int numberOfSides;
    
    public Die(IRandomNumberService generator, int numberOfSides)
    {
        this.generator = generator;
        this.numberOfSides = numberOfSides;
    }
    
    public int Roll()
    {
        return generator.Next(1, numberOfSides);
    }
}

However, using a typical DI framework, like Ninject, you'll quickly discover you get an error. It will complain that it can't bind a value to type Int32. Unfortunately, when Ninject tries to create an instance of Die, it doesn't know what to provide for the numberOfSides argument. In this case you can't even use WithConstructorArgument to specify the value because the number of sides can change at runtime. At this point, I was pretty discouraged; I just gave up and started passing the number of sides to the Roll method. cheater

Back on the horse again, I decided I could create Die with a factory.

public class DiceBag
{
    private readonly IRandomNumberService generator;
    
    public DiceBag(IRandomNumberService generator)
    {
        this.generator = generator;
    }
    
    public Die GetDie(int numberOfSides)
    {
        return new Die(generator, numberOfSides);
    }
}

In this example, using a factory is a decent solution. The only negative is that we have to pass along any dependencies (IRandomNumberService). In a real-world application, the number of dependencies can be much larger. Passing these along means you need to essentially list your dependencies twice: once in the class and once in the factory. If your factory is responsible for creating instances across multiple classes, your factory needs every dependency of every class.

The trick is to give the factory access to the DI framework. With Ninject, the solution is to pass an IKernel to the factory. We can then use the ConstructorArgument class to specify what the missing argument should be:

public Die GetDie(int numberOfSides)
{
    return Kernel.Get<Die>(new ConstructorArgument("numberOfSides", numberOfSides));
}

This is a pretty slick solution. The only negative is that your factory class (and containing project) has to have a dependency on the DI framework. This is not a bad thing. A DI framework is probably the one dependency you shouldn't be too concerned about referencing directly. The piece that bothers me is that the name of the argument has to be hard-coded in a string. Unfortunately, C# doesn't yet provide a mechanism to grab constructor argument names (or does it?).

One option to get around all this fancy injection is to promote the numberOfSides parameter to a property. Let's start by extracting the interface from Die:

public interface IDie
{
    int NumberOfSides { get; }
    
    int Roll();
}

Then the Die class looks like this:

public class Die : IDie
{
    private readonly IRandomNumberService generator;
    
    public Die(IRandomNumberService generator)
    {
        this.generator = generator;
    }
    
    public int NumberOfSides { get; set; }
    
    public int Roll()
    {
        return generator.Next(1, NumberOfSides);
    }
}

Notice that we're exposing a public setter for the property. The code that uses the Die will always see it through the IDie interface, so the setter will not be visible. It's a bit for some purists to accept, but this solution eliminates the need for any special DI framework magic. Furthermore, you can reference the DI framework using the System.IServiceProvider interface. This is what the final factory class looks like:

public class DiceBag
{
    private readonly IServiceProvider serviceProvider;
    
    public DiceBag(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }
    
    public IDie GetDie(int numberOfSides)
    {
        Die die = serviceProvider.GetService(typeof(Die));
        die.NumberOfSides = numberOfSides;
        return die;
    }
}

For Ninject, the IKernel interface implements IServiceProvider out-of-the-box. You still need to provide a binding to say how to resolve IServiceProvider: kernel.Bind<IServiceProvider>().ToConstant(kernel);. This pattern of using constructor arguments for dependencies and properties for runtime variables works really well.

Once the factory pattern is in your toolbox, you can really open up the full power of object-oriented programming in a real world application. Just keep in mind that too many factories can lead to obscure code. Check out this interesting discussion about how factory methods can lead to excessive complexity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment