Design Patterns: Factories are a Function of the Consumer

Sometimes i think I spend my time on this blog just trying to explain the thinking of people much smarter than me.  This is definitely one of those cases.  I’ve probably learned more about programming from reading the Retlang source code than any other.  It’s both remarkably free of compromises and easy to read.  Sometimes you learn not from any individual snapshot, but from the evolution of the code base.  For instance, here was the process context factory in Retlang 0.3:

    public interface IProcessContextFactory : IThreadController, IObjectPublisher
        IMessageBus MessageBus { get; }
        IProcessContext CreateAndStart();
        IProcessContext Create();
        IProcessContext CreateAndStart(ICommandExecutor executor);
        IProcessContext Create(ICommandExecutor executor);
        IProcessContext CreateAndStart(string threadName);
        IProcessContext Create(string threadName);
        IProcessContext CreateAndStart(ICommandExecutor executor, string threadName);
        IProcessContext Create(ICommandExecutor executor, string threadName);
        IProcessBus CreatePooledAndStart(ICommandExecutor executor);
        IProcessBus CreatePooledAndStart();
        IProcessBus CreatePooled(ICommandExecutor executor);
        IProcessBus CreatePooled();

Even ignoring the extra inherited interfaces, this is a pretty big interface.  There’s 11 different ways to create 2 different objects.  Now, when I decided to use this, I created my own interface:

    public interface IRetlangFactory : IDisposable, IHaltable, IChannelCreator
        ICommandTimer CreateCommandTimer(bool isHaltable);

        ICommandQueue CreateContext();

        ICommandQueue CreateContext(ICommandExecutor executor);

        void Stop(ICommandQueue queue);

        void Stop(ICommandTimer timer);

        void WaitForQueues(IEnumerable<ICommandQueue> queues);
        void WaitForTimers(IEnumerable<ICommandTimer> queues);

So, it’s a smaller interface that does more.  I’m not holding this up as an example of great design: it understands ISP about as well as I understand Xhosa.  But from 11 methods to create objects, I’ve got only three.  What isn’t obvious is things like CreateContext always starts a queue at the same time.

So why is this interface so much smaller than the Retlang one?  It’s pretty simple:  I knew which methods I needed.

Still Getting Schooled

This might seem like an insurmountable problem for a library writer, but it isn’t.  By Retlang 0.4, the interface just simply wasn’t there.  Graham Nash had resolved the problem with a couple of hints from Alexander the Great.  So, in the old days my factory called through to Retlang’s factory.  Now, my standard “make a thread” method now reads like this:

    var fiber = new ThreadFiber(new DefaultQueue(_haltableExecutor));
    return fiber;

The haltable executor is actually from my very first post from two and a half years ago.  Of course, if I hadn’t wanted to change the default behaviour, I could have just used the empty constructor, because that implements some sensible defaults.  This technique is decried in some quarters as being “poor man’s IoC”, but I fail to see the problem.  You can write your own factory, or configure a DI container to do the construction for you.  Either way, you’ve lost no flexibility.

So, I think all in all, there’s no point in defining a factory interface within your library unless you’re defining an injection point for your own code.  Controller Factories in ASP.NET MVC are an example of this.  On the other hand, it’s hard to see why IWindsorContainer exists. 

FOOTNOTE:  For design patternistas, you’ll observe that I’m making no distinction between factories, abstract factories or builders.  Truth be told, I’m not seeing many codebases that bother either.

Published by

Julian Birch

Full time dad, does a bit of coding on the side.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s