Improve Code with Static Factory Method instead of Constructor

Sometimes I have problems with my own code. I wrote a piece of code. Some months later, when I wanted to use them in other places, I had to look at their implementation. Other time, I had to dig deeply into my poor memory to find out why I wrote them.

Obviously, there is a need to improve the code quality__the communication. What? Code communicates?

I like to see code in this way

Code is a mean of communication.

When we write a piece of code, either a framework, a library, a class, a method, or even a line of code; it supplies a mean of communication between us and

  1. Compiler: We tell the compiler what and how to translate into binary code.
  2. Computer: The binary code will then instruct the computer does our business logic (our purposes)
  3. Developers: They consume our code, our API

The code does more than our initial thoughts.

Let’s improve our communication mean. A long road starts with the first step. Let’s start with improving constructor.

Note: I write in the context of C# (.NET) environment. You should find similar concepts in yours.

Constructor

As a developer, we have seen this code a lot

    /// <summary>
    /// A person with Name and Assigned To
    /// </summary>
    public class Person
    {
        /// <summary>
        /// Person name
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Assigned to (such as project name, task name).
        /// </summary>
        public string AssignedTo { get; set; }

        public Person()
        {
            
        }

        public Person(string name, string assignedTo)
        {
            this.Name = name;
            this.AssignedTo = assignedTo;
        }
    }

A very simple person class. Assuming that I want to use it in Project/Task

    public class Project
    {
        public string Name { get; set; }
        public IList<Person> People { get; set; }

        public void AddPerson(string name)
        {
            var person = new Person
            {
                Name = name,
                AssignedTo = this.Name
            };
            People.Add(person);
        }
    }

    public class Task
    {
        public string Name { get; set; }
        public Person Responsible { get; set; }

        public void AssignTo(string person)
        {
            var responsible = new Person
            {
                Name = person,
                AssignedTo = this.Name
            };
            Responsible = responsible;
        }
    }

A person is used for people involved in a project. And it is used as a responsible for a task.

So far so good. The Person class has 2 constructors to use. However, if we look a bit deeper, there is a small issue that I usually call

It does not communicate its purposes well.

How do you know what purposes, under what context the object is created for?

Static Factory Method

How about creating an object with a clear purpose? What do you think about this code?

    /// <summary>
    /// A person with Name and Assigned To
    /// </summary>
    public class Person
    {
        /// <summary>
        /// Person name
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Assigned to (such as project name, task name).
        /// </summary>
        public string AssignedTo { get; set; }

        public Person()
        {
            
        }

        public Person(string name, string assignedTo)
        {
            this.Name = name;
            this.AssignedTo = assignedTo;
        }

        public static Person CreatePersonOnProject(string person, string project)
        {
            return new Person(person, project);
        }

        public static Person CreateTaskResponsible(string person, string task)
        {
            return new Person(person, task);
        }
    }

    public class Project
    {
        public string Name { get; set; }
        public IList<Person> People { get; set; }

        public void AddPerson(string name)
        {
            People.Add(Person.CreatePersonOnProject(name, this.Name));
        }
    }

    public class Task
    {
        public string Name { get; set; }
        public Person Responsible { get; set; }

        public void AssignTo(string person)
        {
            Responsible = Person.CreateTaskResponsible(person, this.Name);
        }
    }

There are 2 explicit static factory methods:

  1. CreatePersonOnProject
  2. CreateTaskResponsible

Can you read the code again in Project.AddPerson and Task.AssignTo methods?

On the implementation side, we have not changed anything at all. We construct the same object in both cases. However, we have gained a huge benefit: Communicate the purpose explicitly.

  • Construct a new object (same as new approach)
  • Communicate exactly the purpose.
  • Consumers can use the code without confusion.

Which one should you choose? Both. As a developer, we should choose our tools wisely.

What should we do to improve our decision (decide when to use what)? One of a trick I usually use is that I ask this question

What do the other guys think? Is it clear for them to consume my code?

If the answer is “No”; no, they have to read my code to understand and use it; I will choose “Static Factory Method” approach.

 

There is no perfect code. But we can improve it over the time. When time comes, you will say thank you to yourself; other developers will say thank you to you.

You might find these posts interesting:

Give If Condition a name

Small things matter: Naming

 

    • thaianhduc

      Yes. Good catch! It should be private or at least protected. However, I want to keep it public to prove a point: It is an improvement. There are cases that you might want to use constructors. If in a real project, product code, I agree we should try to avoid public constructor as much as we can.

    • thaianhduc

      There should not in production code. However, I have seen so many code that has public parameterless constructor 🙁 That is a bad habit because it is so easy to end up with invalid state object.

Write a Comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.