Software development

Weapons of mass construction

There is a follow-up post: Advanced weapons of mass construction

Last time I wrote about how to use interfaces to their full potential, but I never really mentioned factories beyond the fact, that we probably need them. This is going to be another chapter to the previous post with a more in-depth look into the matter of encapsulated object creation in C#. Again I will start with what I usually find in a client’s project, and then I will move in small steps towards a progressively more mature implementation. Every step will include a bit of code to demonstrate the changes that were made.

Making up a good example to demonstrate factories is surprisingly hard. There is a multitude of possible scenarios available, but most are too complex to be useful in a blog post. I have settled for a compact and simple project of animals laying eggs and eggs hatching animals, called the AnimalFarm. This kind of logic probably isn’t anything a developer will ever come across, but it should get the point of factories across alright. It also answers whether the hen or the egg was first. As far as C# is concerned a factory created both.

Basic factory implementation

Tying in with my last post, the initial setup has both contracts and implementations separated into their individual assemblies. All concrete classes are made internal and are instantiated through a static factory class. As already mentioned, there are eggs and animals, each of which can produce the other and so forth. Concrete implementations are limited to chickens and their eggs for now, but there will be more further down the line.

public interface IEgg {
    ICreature Hatch();
}

public interface ICreature {
    IEgg Lay();
}
internal class ChickenEgg : IEgg {
    public ICreature Hatch() => Factory.CreateCreature();
}

internal class Chicken : ICreature {
    public IEgg Lay() => Factory.CreateEgg();
}

public static class Factory {
    public static IEgg CreateEgg() => new ChickenEgg();
    public static ICreature CreateCreature() => new Chicken();
}

Mocking a factory

There is one big flaw immediately obvious to the naked eye and that is the inability to mock the factory in a test. Introducing mock ability to the factory requires introducing a contract that can be implemented, as well as scratching the static keywords on the factory class. The factory must now be instantiated for use, using its public constructor.

public interface IFactory {
    IEgg CreateEgg();
    ICreature CreateCreature();
}
internal class ChickenEgg : IEgg {
    public ICreature Hatch() => new Factory().CreateCreature();
}

internal class Chicken : ICreature {
    public IEgg Lay() => new Factory().CreateEgg();
}

public class Factory : IFactory {
    public IEgg CreateEgg() => new ChickenEgg();
    public ICreature CreateCreature() => new Chicken();
}

Factoryception

Since the contract was introduced and factories are nothing special, there should also be a factory that handles instantiation of the new contract. The static factory class is back, and it’s now creating internal factories. The last two steps may seem odd, but I have included them as separate stages of development to showcase the reasoning behind it.

internal class ChickenEgg : IEgg {
    public ICreature Hatch() => Factory.Instance.CreateCreature();
}

internal class Chicken : ICreature {
    public IEgg Lay() => Factory.Instance.CreateEgg();
}

public static class Factory {
    public static IFactory Instance => new InternalFactory();
}

internal class InternalFactory : IFactory {
    public IEgg CreateEgg() => new ChickenEgg();
    public ICreature CreateCreature() => new Chicken();
}

A concrete factory for a concrete type

To bring some variety into the farm, I have decided to introduce the ostrich as a new creature. There is no technical difference between chicken and ostrich in this case, since the exercise is about creating stuff, not doing something useful with it. The ostrich, its eggs and the new factory are exact copies of the chicken versions with different naming and types. The internal factory has now duplicated to create either chickens and their eggs or the new ostrich variants.

internal class ChickenEgg : IEgg {
    public ICreature Hatch() => Factory.Chicken.CreateCreature();
}

internal class Chicken : ICreature {
    public IEgg Lay() => Factory.Chicken.CreateEgg();
}

internal class OstrichEgg : IEgg {
    public ICreature Hatch() => Factory.Ostrich.CreateCreature();
}

internal class Ostrich : ICreature {
    public IEgg Lay() => Factory.Ostrich.CreateEgg();
}

public static class Factory {
    public static IFactory Chicken => new ChickenFactory();
    public static IFactory Ostrich => new OstrichFactory();
}

internal class ChickenFactory : IFactory {
    public IEgg CreateEgg() => new ChickenEgg();
    public ICreature CreateCreature() => new Chicken();
}

internal class OstrichFactory : IFactory {
    public IEgg CreateEgg() => new OstrichEgg();
    public ICreature CreateCreature() => new Ostrich();
}

Atomizing a factory to method level

I’m a huge fan of out-parameters when it comes to creating objects in C#, even if it means writing a few more lines of code. If used on methods in an interface, any type can ‘overload’ a method from a generic contract by implementing the contract several times. In this case, the creating factory methods can be named identically and be distinguished by the type of the out-parameter. This wasn’t possible before, since the return type isn’t part of a method’s signature. There are other ways to achieve this, but I feel that this is the cleanest.

public interface IFactory : IFactoryCreate<IEgg>, IFactoryCreate<ICreature> { }

public interface IFactoryCreate<T> {
    void Create(out T obj);
}
internal class ChickenEgg : IEgg {
    public ICreature Hatch() {
        Factory.Chicken.Create(out ICreature obj);
        return obj;
    }
}

internal class Chicken : ICreature {
    public IEgg Lay() {
        Factory.Chicken.Create(out IEgg obj);
        return obj;
    }
} 

internal class OstrichEgg : IEgg {
    public ICreature Hatch() {
        Factory.Ostrich.Create(out ICreature obj);
        return obj;
    }
}

internal class Ostrich : ICreature {
    public IEgg Lay() {
        Factory.Ostrich.Create(out IEgg obj);
        return obj;
    }
}

internal class ChickenFactory : IFactory {
    public void Create(out IEgg obj) => obj = new ChickenEgg();
    public void Create(out ICreature obj) => obj = new Chicken();
}

internal class OstrichFactory : IFactory {
    public void Create(out IEgg obj) => obj = new OstrichEgg();
    public void Create(out ICreature obj) => obj = new Ostrich();
}

Factory injection

This last step has introduced a lot of boilerplate code, and I’m afraid it will get worse before it gets better. The time has finally come to have some fun with evolution, and start injecting ostriches into chickens. So far, every concrete type has created objects using just one specific factory. A factory property must be added to the contracts, so that any outsider can manipulate the object spawning process at runtime. This process is called dependency injection, where logic is wrapped into objects and objects can be replaced easily.

public interface IEgg {
    IFactoryCreate<ICreature> Factory { get; set; }

    ICreature Hatch();
}

public interface ICreature {
    IFactoryCreate<IEgg> Factory { get; set; }

    IEgg Lay();
}
internal class ChickenEgg : IEgg {
    public IFactoryCreate<ICreature> Factory { get; set; } = new ChickenFactory();

    public ICreature Hatch() {
        Factory.Create(out ICreature obj);
        return obj;
    }
}

internal class Chicken : ICreature {
    public IFactoryCreate<IEgg> Factory { get; set; } = new ChickenFactory();

    public IEgg Lay() {
        Factory.Create(out IEgg obj);
        return obj;
    }
} 

internal class OstrichEgg : IEgg {
    public IFactoryCreate<ICreature> Factory { get; set; } = new OstrichFactory();

    public ICreature Hatch() {
        Factory.Create(out ICreature obj);
        return obj;
     }
}

internal class Ostrich : ICreature {
    public IFactoryCreate<IEgg> Factory { get; set; } = new OstrichFactory();

    public IEgg Lay() {
        Factory.Create(out IEgg obj);
        return obj;
    }
}

Outsourcing the base logic into an abstract layer

The boilerplate code is now really out of hand, and it grows with every new concrete implementation. A little refactoring can reduce that by adding a producer contract and implementing the core logic into abstract base classes. The base classes should be outsourced to another assembly, thus adding a reusable layer to the code base. All concrete classes shrink in size and complexity, which increases maintainability. It should be noted, however, that this allows referencing an abstract base class instead of the contract. A minor downside compared to the benefits, but it’s something to be considered beforehand, as it can reintroduce the risk of unwanted dependencies.

public interface IProducer<T> {
    IFactoryCreate<T> Factory { get; set; }
}

public interface IEgg : IProducer<ICreature> {
    ICreature Hatch();
}

public interface ICreature : IProducer<IEgg> {
    IEgg Lay();
}
public abstract class Producer<TOut, TFactory> : IProducer<TOut>
    where TFactory : IFactoryCreate<TOut>, new() {

    public IFactoryCreate<TOut> Factory { get; set; }

    public Producer() => Factory = new TFactory();

    protected TOut Produce() {
        Factory.Create(out TOut obj);
        return obj;
    }
}

public abstract class Egg<TFactory> : Producer<ICreature, TFactory>, IEgg
    where TFactory : IFactoryCreate<ICreature>, new() {

    public ICreature Hatch() => Produce();
}

public abstract class Creature<TFactory> : Producer<IEgg, TFactory>, ICreature
    where TFactory : IFactoryCreate<IEgg>, new() {

    public IEgg Lay() => Produce();
}
internal class ChickenEgg : Egg<ChickenFactory> { }

internal class Chicken : Creature<ChickenFactory> { }

internal class OstrichEgg : Egg<OstrichFactory> { }

internal class Ostrich : Creature<OstrichFactory> { }

Extending a factory

There is just one more thing to do and that is allowing for the factory mechanism to be extended at runtime. Renaming the factory interface to IAnimalFactory makes its purpose much more apparent, while freeing up the original name for another task. The now unused IFactory is implemented by the resurrected internal factory, which reappears in the abstract layer along with the static factory class. New animal factories are attached to the factory interface using extension methods, and can be used to shove a T. rex egg into a chicken from which an ostrich may hatch.

The following is the collective code base of the little evolution defying animal farm. The full demo code can also be found on GitHub.

public interface IProducer<T> {
    IFactoryCreate<T> Factory { get; set; }
}

public interface IEgg : IProducer<ICreature> {
    ICreature Hatch();
}

public interface ICreature : IProducer<IEgg> {
    IEgg Lay();
}

public interface IAnimalFactory : IFactoryCreate<IEgg>, IFactoryCreate<ICreature> { }

public interface IFactoryCreate<T> {
    void Create(out T obj);
}

public interface IFactory { }
public abstract class Producer<TOut, TFactory> : IProducer<TOut>
    where TFactory : IFactoryCreate<TOut>, new() {

    public IFactoryCreate<TOut> Factory { get; set; }

    public Producer() => Factory = new TFactory();

    protected TOut Produce() {
        Factory.Create(out TOut obj);
        return obj;
    }
}

public abstract class Egg<TFactory> : Producer<ICreature, TFactory>, IEgg
    where TFactory : IFactoryCreate<ICreature>, new() {

    public ICreature Hatch() => Produce();
}

public abstract class Creature<TFactory> : Producer<IEgg, TFactory>, ICreature
    where TFactory : IFactoryCreate<IEgg>, new() {

    public IEgg Lay() => Produce();
}

public static class Factory {
    public static IFactory Instance => new InternalFactory();
}

internal class InternalFactory : IFactory { }
internal class ChickenEgg : Egg<ChickenFactory> { }

internal class Chicken : Creature<ChickenFactory> { }

internal class OstrichEgg : Egg<OstrichFactory> { }

internal class Ostrich : Creature<OstrichFactory> { }

internal class ChickenFactory : IAnimalFactory {
    public void Create(out IEgg obj) => obj = new ChickenEgg();
    public void Create(out ICreature obj) => obj = new Chicken();
}

internal class OstrichFactory : IAnimalFactory {
    public void Create(out IEgg obj) => obj = new OstrichEgg();
    public void Create(out ICreature obj) => obj = new Ostrich();
}

public static class IFactoryExtensions {
    public static IAnimalFactory Chicken(this IFactory @this) => new ChickenFactory();
    public static IAnimalFactory Ostrich(this IFactory @this) => new OstrichFactory();
}
internal class RaptorEgg : Egg { }

internal class Raptor : Creature<RaptorFactory> { }

internal class TRexEgg : Egg<TRexFactory> { }

internal class TRex : Creature<TRexFactory> { }

internal class RaptorFactory : IAnimalFactory {
    public void Create(out IEgg obj) => obj = new RaptorEgg();
    public void Create(out ICreature obj) => obj = new Raptor();
}

internal class TRexFactory : IAnimalFactory {
    public void Create(out IEgg obj) => obj = new TRexEgg();
    public void Create(out ICreature obj) => obj = new TRex();
}

public static class IFactoryExtensions {
    public static IAnimalFactory TRex(this IFactory @this) => new TRexFactory();
    public static IAnimalFactory Raptor(this IFactory @this) => new RaptorFactory();
}

Tyrannosaurus Factorius Rex

As with any optimization process, the refactoring of code can be a never-ending story. There is always a reason to add more layers, more abstraction and more flexibility. Sometimes it is more important to know what can be done and how, rather than actually doing it. It may even make sense – in a very rare edge case – to have a deeper level than just a factory of factory factories.

In the end it comes down to what is reasonable and what isn’t. In most cases, the most basic implementations are just not good enough and developers should aim a little higher. But that doesn’t mean that it’s always going to have to be the moon, since that will cost time, money and sometimes your sanity or a goat sacrifice. In laying out all the individual steps, I hope to simplify the transfer from a very crude object creation process to a properly advanced one. It also demonstrates that sometimes, code can move back and forth several times during a complex refactoring. There is no perfect result right away in software development, code needs to mature and settle.

Leave a Reply

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