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: (we will update the table of contents as we progress)

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:

No doubt, the following code would work:

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

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.

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.

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.

This entry was posted in How-To's and tagged , , , , . Bookmark the permalink.

13 Responses to Every method begins with “new” – Code smells series

  1. Then I assume you would set the constructors to private so that the factory methods are the only way to initialise the class. I see the advantage, you are not having to look through the match class to see what the 2 constructors do, it is obvious from the method names what function they perform. It would be good if constructors could be amended to take an alternative method name, so that we could have factory methods without the need for the extra code.

  2. Anthony Terra says:

    I take any direct invocation of “new” as a code smell. I prefer dependancy inversion to unless you are dealing with internal classes. While some will say don’t go interface crazy and prefer “has a” vs “is a” relationships. I find if I don’t use an interface and dependancy injection to create objects I almost invariably paint myself into a corner.

    • mvonballmo says:

      > I find if I don’t use an interface and dependency injection to create objects I almost invariably paint myself into a corner.

      Agreed. I’ve since stopped resisting and design almost everything with injection and factories. It’s very liberating.

  3. Gebb says:

    > it is recommended that they … never throw exceptions

    Recommended by whom? What if an invalid argument is specified? I think this recommendation is wrong.

  4. Lars Michael says:

    “Constructors are not functional methods and it is recommended that they … never throw exceptions.”

    I believe it is a perfectly legitimate principle to add guard clauses to the constructor – for example throwing an ArgumentNullException in the case of an unexpected null argument in the constructor.

  5. Pingback: Dew Drop - July 24, 2018 (#2772) - Morning Dew

  6. Oddbjørn Bakke says:

    What is wrong with using XML documentation instead, and multiple constructors?

    It’s cleaner, and easier to read (as you now can use whitespaces, references etc.). Should not bloat up other things like reflection. And will not mix together with other static methods.

    Is there any other difference then, than having the possibility for multiple factory methods with same parameters, and async factory?

    Factory is just a ctor with a little more more Fa and y…

    • Tomaso Pye says:

      If someone took the time to read your documentation and has an IDE that includes intellisense, and you remember to keep it up to date, then great.

      But just from reading the code, which is the only accurate specification of what your system does, being able to say (per the example) Match.ResumeMatch(1,2) will always be more expressive than new Match(1,2). There is no need to hover over it and read intellisense to be sure of what the constructor call is intended to do.

      Also, the bit at the end of the article is where the best use of Factory pattern comes in, Dependency Inversion (that’s the D on SOLID), which is implemented via Dependency Injection/Inversion of Control libraries like Castle, SimpleInjector, Ninject etc.

      Certainly new is good enough for your personal code. But anything to do with real-world software development, patterns will win out.

  7. Scott Hannen says:

    I like this a lot. Anything that makes code more expressive is good. “new” says that you need a new one, but this describes your thinking on *why* you would create a new one.

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

  8. Ulrich says:

    @Dino: Ehm… The is a special “Replace constructor with factory method” refactoring in ReSharper.

  9. Dino Esposito says:

    Thanks everybody for taking the time to read and share your thoughts. I’m really glad that this happened! Overall, any recommendations or “commonly agreed recommendations” are, at the very end of the day, just … recommendations. From dictionary, it is “a suggestion or proposal as to the best course of action”. And recursively one could go on and argue what “best” is. In only a few iterations, we’re all stuck in the classic “It depends”.
    So specifically for factories and constructors, I believe whatever option you opt, whether or not you throw exceptions or not, as long as the overall code works it’s OK. I don’t think constructors and factories have a deep impact on the maintenance like other aspects such as dependency injection and overall expressivity achieved by composing together in ad hoc classes data that always go together. Yet constructors are too low level details seen from the perspective of domain but no tool (not event R#) I believe can automatically replace human judgement when it comes to introducing a factory.

  10. Ian Yates says:

    I’m a big fan of using expressively named factory methods too.

    Another advantage is that if the instantiation of the resource requires some asynchronous effort, you can make your factory method async and return Task. That’s much better than constructing a class and then having an unspecified contract that the consumer of the class needs to wait for some property to be signalled before the class can be properly used.

Leave a Reply

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