Software development

Advanced weapons of mass construction

The creation of objects is one of the most popular topics in software development. It’s that popular because there is a vast range of possibilities and almost every road leads to Rome. But different projects have different needs, and the same is true for organizations and target audiences. Whatever works well in one spot may be either too complex or too crude in another, and here is where things get tricky. Developers have to have a good understanding of the requirements and how the code will eventually be used. And that has to happen before the actual object creation mechanism is planned and implemented.

In the world of dotnet, the most common way of creating an object is calling a constructor. This is as simple as it gets, and many developers rely on this mechanism throughout their software. The other end of the object creation complexity scale is the home of dependency injection and dependency inversion. This is the world of very abstract mechanisms that allow much more than just interchanging of implementations. It’s a topic so complex that it deserves at least a few articles for itself, so I’m not going down that rabbit hole in this text. Instead, I want to talk about the middle ground that is factories. I’ve written about them before and this is a follow-up, describing what my factories look like and the reasoning behind it.

Quick refresher on factories

The main purpose of factories is to decouple consumer code from specific implementations and their dependencies when using another component. If done right, a calling consumer code will only refer to interfaces, delegates, enumerations and primitive structs and classes. This collection of types is called the API – or the contract – of a component, and it defines everything that is necessary for interactions with the component. If done even better, the contents of the contract do not implement any business logic and thus are purely descriptive in nature. And if done perfectly, the only dependencies of the contract are members of the base class library.

A factory is the connecting link between such a contract, and it’s implementation. In other words, a factory creates an object that satisfies a specific contract, while also hiding the exact type that was created. But a factory is also an object in itself and can therefore be interchangeable as well.

The factory everyone should hate

This is most likely one of the most horrible factory implementations that I can imagine. I don’t doubt that people can think of even worse implementations, but I feel that this one is about as bad as it gets. There is a multitude of problems with this factory, yet I see this a lot, and it scares the pants off of me. It also scares me that developers rarely recognize the issues for what they are. But let’s start with what is immediately obvious.

public static class Factory {
    public static ICar CreateCar(Int32 gears) => new Car(gears);
    public static IBus CreateBus(Int32 seats) => new Bus(seats);
    public static IYacht CreateYacht(Int32 propellers) => new Yacht(propellers);
    public static IOutriggerCanoe CreateOutriggerCanoe() => new OutriggerCanoe();
}

This factory is static, so you can’t create instances of it for passing around to other objects. And because it doesn’t implement a contract, you can’t just replace it or mock it in tests. Wherever it is used, all the dependencies that come with VehicleModule.dll will be introduced to the calling code as well. Then there are the factory method names that also include the type name. Every time a type is renamed, the method has to be realigned to avoid confusing moments for future developers trying to make sense of it. The combination of everything mentioned above also makes it impossible to inject just parts of the factory. When an object uses the factory to create cars and busses, it will also be able to create boats, which might break isolation. And last but not least, such a factory is only available for a given component, and every component needs its own factory implementation. As a result, the code will be cluttered with calls to various different factory classes, depending on what types are instantiated.

One factory to create them all

The implementation that I outlined in my last article on the matter (weapons of mass construction) has been my go-to solution to all the issues mentioned above for several years. It turns out that, whenever I think about factories hard enough or when I use them a lot in a project, this is always the end result. And because I’ve been implementing factories that way so many times, I’ve decided that it’s time to cast it all into a reusable package. But I’ve tried to cover more than just factories, since they aren’t the only example for injectable object creation. Others are parsers and conversion mechanisms where new objects are created using input parameters. The interface IFactoryCreate is now called ICreator to have it match the other use cases as well, and to put an emphasis on the task at hand. I’ve also added methods that may never throw exceptions using the Try-Pattern, including an option that returns any caught exception.

public interface ICreator<TOut> {
    void Create(out TOut obj);
    Boolean TryCreate(out TOut obj);
    Boolean TryCreate(out TOut obj, out Exception ex);
}

// interfaces with 1-7 parameters ...

public interface ICreator<TOut, TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TIn8> {	
    void Create(out TOut obj, TIn1 in1, TIn2 in2, TIn3 in3, TIn4 in4, TIn5 in5, TIn6 in6, TIn7 in7, TIn8 in8);		
    Boolean TryCreate(out TOut obj, TIn1 in1, TIn2 in2, TIn3 in3, TIn4 in4, TIn5 in5, TIn6 in6, TIn7 in7, TIn8 in8);		
    Boolean TryCreate(out TOut obj, TIn1 in1, TIn2 in2, TIn3 in3, TIn4 in4, TIn5 in5, TIn6 in6, TIn7 in7, TIn8 in8, out Exception ex);
}

Now, that looks like a ton of boilerplate code required to implement just a simple factory pattern. To simplify the entire factory creation process, the base factory is capable of creating implementations of all the ICreator interfaces. The heavy lifting is already done in the internal implementation, and this should be sufficient for 99.9% of all scenarios. This makes it easy to dynamically create injectable factories at runtime as well.

public static class IParserExtensions {
    public static ICreator<SemanticVersion, String> SemVer(this IParser _) =>
        Factory.Instance.Creator.Create((String in1) => SemanticVersion.Parse(in1));
}
public static class IFactoryExtensions {
    public static IVehicleFactory Vehicles(this IFactory _) => new VehicleFactory();
}

public interface IVehicleFactory :
    ICreator<ICar, Int32>,
    ICreator<IBus, Int32>,
    ICreator<IYacht, Int32>,
    ICreator<IOutriggerCanoe> { }

internal class VehicleFactory : IVehicleFactory {
    private static ICreator<ICar, Int32> _carFactory = Factory.Instance.Creator.Create<ICar, Int32>((in1) => new Car(in1));

    public void Create(out ICar obj, Int32 in1) => _carFactory.Create(out obj, in1);
    public Boolean TryCreate(out ICar obj, Int32 in1) => _carFactory.TryCreate(out obj, in1);
    public Boolean TryCreate(out ICar obj, Int32 in1, out Exception ex) => _carFactory.TryCreate(out obj, in1, out ex);

    // implementations of IBus, IYacht and IOutriggerCanoe ...
}

A call to Factory.Instance.Vehicles().Create(out ICar obj, Int32 in1) is all it takes to create an instance of ICar, but the names of the input parameters are nothing to write home about. This flaw is introduced by the ICreator interface, and it’s no problem when parsing a string to another type, where the parameter’s meaning is more than obvious. But in this case, Int32 in1 could be anything from horsepower to the overall length of the vehicle. To get around this issue, the public definition of the factory can be done using an abstract class instead of an interface. That even lets you create new XML comments for all members and enables intellisense support for consumers.

public static class IFactoryExtensions {
    public static VehicleFactory Vehicles(this IFactory _) => new InternalVehicleFactory();
}

public abstract class VehicleFactory :
    ICreator<ICar, Int32>,
    ICreator<IBus, Int32>,
    ICreator<IYacht, Int32>,
    ICreator<IOutriggerCanoe> {

    public abstract void Create(out ICar obj, Int32 gears);
    public abstract Boolean TryCreate(out ICar obj, Int32 gears);
    public abstract Boolean TryCreate(out ICar obj, Int32 gears, out Exception ex);

    // abstract implementations of IBus, IYacht and IOutriggerCanoe ...
}


internal class InternalVehicleFactory : VehicleFactory {
    private static ICreator<ICar, Int32> _carFactory = Factory.Instance.Creator.Create<ICar, Int32>((in1) => new Car(in1));

    public override void Create(out ICar obj, Int32 gears) => _carFactory.Create(out obj, gears);
    public override Boolean TryCreate(out ICar obj, Int32 gears) => _carFactory.TryCreate(out obj, gears);
    public override Boolean TryCreate(out ICar obj, Int32 gears, out Exception ex) => _carFactory.TryCreate(out obj, gears, out ex);

    // implementations of IBus, IYacht and IOutriggerCanoe ...
}

All this is published as a NuGet package, and it’s licensed under the MIT licence. It ticks all the boxes that I want my factories to tick, and it spares me the pain of having dozens of static factory classes spread out through my code base. It does shift factories a little towards the DI-related end of the complexity scale, but it’s still a lot less complex than a full-blown DI-container based implementation.

Leave a Reply

Your email address will not be published.