Dotnet logo

.NET Tools

Essential productivity kit for .NET and game developers

How-To's

Every method begins with “new” – Code smells series

This post is part of a 10-week series by Dino Esposito (@despos) around a common theme: code smells and code structure.

Last week, we looked at additional ways of bringing the code language closer to the business language. Today, we will discuss the usage of new vs. factory methods, and if we can make our business logic even more understandable.

In this series:

Constructors and factories

In object-oriented languages, common ways to get a new instance of a type are using a constructor or using a factory method.

A constructor is a method in the class that enjoys special treatment from the compiler, and is defined with a specific syntax compared to other methods. In both Java and C#, the constructor is a method that has the same name as the class and can’t be called explicitly on an existing instance, unless via reflection or via System.Runtime.

A factory method is a – typically static – method that returns an instance of the class. Internally, a factory method uses a constructor, whether publicly exposed or protected.

Since the very early days of object-oriented programming, constructors have been the preferred and most common way to create new instances of types. Constructors are simple to understand and trivial to implement. So why should their use be questioned?

Constructors are easy and natural to use as everyone learns object-oriented programming beginning with constructors, but they are hardly business-specific. In addition, constructors are unnamed methods that are only distinguished by length and structure of the parameter list.

In programming, a good practice is to start coding a class using constructors, and then, if you figure out that too much work is required to get new instances, refactor to factory methods. Other situations that warrant this include having too many parameters, too many use-cases, or conditional logic. In such scenarios, a factory method is a good way to encapsulate details.

Constructors are not functional methods and it is recommended that they only contain code that does minimal work and never throw exceptions. Constructors exist since the first day of object-oriented programming, and for many years alternatives looked like fancy things, mostly good for pundits. The advent of domain-driven design (DDD), however, put constructors and their alternative – factory methods – under a different light.

You use constructors to create new instance of classes, but in a DDD scenario you only create instances if you have a business reason. And when you have a business reason to create an instance of a class, then you also have a name for that reason or case. Why then go with an unnamed method?

Let’s say you have a class that represents a sport match, and you’re creating a scoring application. Business-wise, you have at least two scenarios to address: a new match to score from the beginning, and a match that resumes from a known score. In terms of constructors, you can have the following:

public class Match
{
    public Match()
    {
        Point1 = 0;
        Point2 = 0;
    }
 
    public Match(int p1, int p2)
    {
        Point1 = p1;
        Point2 = p2;
    }
 
    public int Point1 { get; private set; }
    public int Point2 { get; private set; }
 
    // More code
}

No doubt, the following code would work:

var newMatch =  new Match();

var resumedMatch =  new Match(1, 2);

Business-wise, though, don’t you think the following is a cleaner and more descriptive approach?

var newMatch =  Match.StartNew();

var resumedMatch =  Match.ResumeFrom(1, 2);

In the end, a series of factory methods explains why you may ever need to have a new instance of that entity.

The proposed example is deliberately simple, but it shows the fundamental reasons for considering refactoring your constructors (or at least some of them) to factory methods. The primary reason is expressiveness and adherence to the business domain.

Turning constructors into factory methods can be manual work, or it can be a task you leave to a refactoring tool. In ReSharper and Rider, for example, we can highlight the lines and use the context menu to Extract Method (Ctrl+R, M).

Extract method from code

A factory method requires a bit of extra work, as you have to mark the method as static and call the (default) constructor.

public static Match StartNew()
{
    var match = new Match
    {
        Point1 = 0,
        Point2 = 0
    };

    return match;
}
 
public static Match ResumeMatch(int p1, int p2)
{
    var match = new Match
    {
        Point1 = p1,
        Point2 = p2
    };

    return match;
}

Sometimes, factory methods might be hard to distinguish from other static methods, making it more difficult to distinguish them from other non-factory static methods. In such cases, you can always group your factory methods into a separate factory class.

public static class MatchFactory
{
    public static Match StartNew()
    {
        // ...
    }

    public static Match ResumeMatch(int p1, int p2)
    {
        // ...
    }
}

In summary, static factory methods have some clear advantages in business logic:

  • Factory methods are named methods
  • Factory methods may use internal optimization techniques such as object pooling
  • Factory methods may return any compatible type

Factory methods also increase readability, let you stay closer to the ubiquitous language of the business domain, and to some extent also reduce code verbosity.

A logical next step would be to look at dependency injection, wouldn’t it? That’s what we will be doing next week!

Download ReSharper 2018.1.2 or Rider 2018.1.2 and give them a try. They can help spot and fix common code smells! Check out our code analysis series for more tips and tricks on automatic code inspection with ReSharper and Rider.

image description