Other awesome posts in the series:

The Legacy Code To Testable Code Series

General patternsAccessibilityDealing with dependenciesAdvanced patterns
IntroductionAdd settersExtract methodStatic constructors (initializers)
RenamingMore accessorsExtract classMore static constructors
Add overloadIntroduce parameterInstance constructors
Testable objectConditionals to guard blocks

So we talked about static constructors and how to go around them. How about instance constructors? Are they innocent or guilty of the same crimes as their static brothers?

I think Java got the terminology right (although not the implementation). What we call a “Constructor” is different than an Initializer. A “constructor” instantiates the object, and an “Initializer” initializes it. These are two operations.

However, in most languages, we do both together. We see a constructor as a place in the code to initialize the object. There’s nothing wrong with that.

Almost.

Sometimes the code in the instance constructor does things that can render the object un-testable. In order to understand why, we need to understand object construction.

Hiding behind a simple “new”, are implicit operations: allocating memory for the object, including building the vtable and the rest of the methods . Only after that, the code in the constructor is called.

Of course, you cannot mock an object until it was constructed. After all you need a reference to the instance handle before you cam mock any of its methods.

Some power-tools can help you choose if to call the constructor code, or just do the allocation. It may be a good enough solution for the problem.

For example:

public class Cache
{
    public Cache()
    {
        InitializeData();
    }
}

Imagine our Cache constructor causing so much problems, we can’t just create an instance without the proper environment. That is a problem to any mocking tool that uses inheritance (or just manual mocking), because the base constructor will be called before it reaches the mocked constructor. We cannot change the behavior of InitializeData whether it’s public or not.

Power tools help if they can override the constructor altogether. However, even they cannot help us in a case like this:

public class Cache
{
    public Cache()
    {
        if (FromDB())
            InitializeDataFromDB();
        else
            InitializeDataFromFile();
    }
}

The FromDB method is an instance method as well. Remember, before the object was created, we can’t access any of its method table. So we can’t mock any method, including FromDB. if it’s called by the constructor.

That means we can’t choose whether to initialize from the database or a file. Once the constructor is invoked, it will run all the way with the actual code.

We can either roll with the constructor as is, or not call it at all. If the constructor is not called, we suffer the consequences of an object that was not initialized at all.

There’s an obvious solution for that: Separate construction from initialization. Like with the static constructor issue, introduce an explicit Initialize method:

public class Cache
{
    public Cache()
    {
    }

    public Initialize()
    {
        if (fromDB())
            InitializeDataFromDB();
        else
            InitializeDataFromFile();
    }
}

This way, we removed the obstacles from the constructor, and after the object creation, we can mock any of the methods. The cost is that the consumer of the object now needs to call the Initialize method.

There’s another slight OO design issue with this solution. An object managing itself, should take care of its initialization, and not depend on its caller to do that. If we used TDD, we’d probably not create a separate method, because we would see the separate initialization call as redundant. We break the design principle, in favor of testability.

The things we have to deal with in software.

 


2 Comments

Arend v. Reinersdorff · January 22, 2016 at 12:19 am

Another option is to split the construction off into a factory.

This gives a clear separation of construction and behavior and is easier to test, But at the cost of another level of indirection and an additional class:

public class CacheFactory {
public Cache createFromDatabase() {
// initialize data from database
// …

return new Cache(data);
}

public Cache createFromFile() {
// initialize data from database
// …

return new Cache(data);
}
}

public class Cache {
public Cache(Data data) {
// …
}
}

    Gil Zilberfeld · January 22, 2016 at 1:39 pm

    Thanks for the comment Arend!
    I usually don’t care about the number of classes. The issue is the indirection, especially when there’s really no need for a factory (e.g. that’s the only cache we have). It’s not that either is really bad, we just need to pick a not-ideal option.

Leave a Reply

Avatar placeholder

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