Overcoming the Limitations of Constructors

by Zoran Horvat
Apr 09, 2019

Each and every object should start off in a complete and consistent state. Once constructed, the object will move through different states during its lifetime. While those changes happen, we will still insist on making subsequent states of the object complete and consistent, too.

If you can make this happen, if you can construct a complete and consistent object, and then make it move through consistent states only, then you will never have to defend against inconsistencies in the object’s internal state.

In this article, we will try to devise a design which prevents construction of inconsistent objects, i.e. objects that should not exist in the first place. We will start from a very simple scheme, throwing an exception from the constructor. Then, we will progress to more complex construction schemes which are avoiding use of exceptions to reach the same goal.

Throwing Exceptions from the Constructor

Let’s assume that we have a factory function which constructs an object.

Func<MyClass> factory = ...;
MyClass obj = factory(args); // throws if args are invalid
obj.DoSomething();

This function would throw an exception if mandatory conditions were not met for an object to be constructed. For instance, the function could test its arguments and throw an exception if their values were not acceptable.

At this stage, it is critical to understand that if consistency criteria were violated before constructing the instance, then the factory function would fail with an exception, meaning that it would not produce the object on the output. Reference assignment would not happen if an exception was thrown. In other words, exception would prevent you from laying hands on an inconsistent or incomplete object. There will never be a reference to an inconsistent object.

That is the great defense, you see. It requires no defensive code at all in any method which consumes the object. Let me say that in form of a rule: If you have the object, then it’s fine.

If it weren’t fine, you wouldn’t have the object in the first place. That is the rule which dismisses any defensive code around the object reference, simply because object reference is good by definition.

We shall continue developing this idea, so let me introduce a more detailed example model. Assume that there are three entities: a student, a subject and a professor. The goal of the next exercise will be to construct an object which represents exam application, which bundles all three of these together.

We will have to defend on several accounts and I will list them here before we try to deal with them. For one thing, to construct an exam application, all components, student, subject and professor, must be non-null objects. Next, student must enroll the semester during which the subject is taught. Also, the student must not have a prior score on the same subject. Finally, the professor must be the one teaching the subject. These are all the rules:

student != null
subject != null
professor != null
student.Enrolled == subject.TaughtDuring
!student.HasPassedExam(subject)
subject.IsTaughtBy(professor)

We have come to the list of six rules that must be satisfied before a student can apply to a certain exam. Now we can plan the defense. If we supposed that object of the ExamApplication class is created using the constructor, then the constructor would throw an exception should any of the conditions be violated.

Understanding the Limitations of Constructor Validation

When implementing constructor validation, it is common to throw exceptions like ArgumentNullException when arguments are null, ArgumentException if more complex rules are violated for concrete argument values, and so on. Below is the implementation of one such constructor, when its validation is based on throwing exceptions. Don’t get me wrong about throwing from a constructor. If you think this through, you will see that constructor has no other options than to throw when it discovers that something is wrong.

class ExamApplication
{
  public Student Candidate { get; }
  public Subject Subject { get; }
  public Professor AdministeredBy { get; }

  public ExamApplication(
    Student candidate,
    Subject subject,
    Professor administeredBy)
  {
    if (candidate == null)
      throw new ArgumentNullException(nameof(candidate));

    if (subject == null)
      throw new ArgumentNullException(nameof(subject));

    if (administeredBy == null)
      throw new ArgumentNullException(nameof(administeredBy));

    if (candidate.Enrolled != subject.TaughtDuring)
      throw new ArgumentException();

    if (candidate.HasPassedExam(subject))
      throw new ArgumentException();

    if (!subject.IsTaughtBy(administeredBy))
      throw new ArgumentException();

    this.Candidate = candidate;
    this.Subject = subject;
    this.AdministeredBy = administeredBy;
  }
}

From this point on, I plan to start advocating against throwing exceptions. What’s wrong with exceptions thrown from constructors, you may ask? The truth is that someone was in need of an ExamApplication object, and that someone has invoked the constructor in good faith, expecting to have an object after that.

ExamApplication application =
  new ExamApplication(me, physics, professor);

The problem the caller is facing is that the application reference will not be set, nor will this code segment continue running, for that matter. By throwing an exception, we are not creating an object. That is a bad thing if you consider that the application was expecting that object to continue executing. Now that the object was not constructed, the whole application request will stop.

This situation is indicative of a limitation that comes with the concept of constructors. Constructor cannot tell in advance whether it will succeed or not. We can only invoke it and hope for the best.

ExamApplication application = new ExamApplication(me, physics, professor);
// Hope we have reached this stage
DealWith(application);

Defending from a constructor which communicates rule violations by throwing exceptions boils down to handling exceptions:

try
{
    ExamApplication application = new ExamApplication(me, physics, professor);
    // Hope we have reached this stage
    DealWith(application);
}
catch (Exception)
{
    // Now... what?
}

The catch block says it all. An exception was thrown, and then – what? A lesser problem here is that we are employing a heavy mechanism, exception handling, to control one tiny bit of behavior. If only we could ask the constructor if everything is alright with the arguments before invoking it, we would know in advance that there is a problem, and we wouldn’t even invoke the constructor. There would be no exception to catch in the first place. Alas, constructors do not let us do such things. And then we need to devise a different way to ask questions before constructing an object.

The greater problem here is that exceptions are dynamically scoped. There is no guarantee that any concrete exception object that we might catch has truly originated in the constructor itself, rather than in an unknown function invoked from inside the constructor. Even worse, if the constructor caller forgot to handle the exception, someone else would catch it. Would that entity be able to understand the meaning of this exception? It might make a guess, but, as it comes to be, guessing the meaning of messages sometimes turns incorrect, and then it manifests as a bug.

Controlling execution flow via exceptions is a terrible idea. It introduces parallel communication channels between functions, which sometimes break after the code is modified. We already have one communication channel – function return values – and we should stick to that channel when it comes to passing messages back from the callee to the caller.

From this point on, we will examine several possibilities that are still left at our disposal, which are avoiding the need to throw exceptions. The most important takeaway here is in “avoiding the need” part. I will aim at designs which are making it unnecessary to defend from a failed object construction.

Promoting Constructor to a Builder

Imagine there is some method, named Alright, which tells whether arguments are alright for the ExamApplication’s constructor:

if (Alright(professor, subject, student))
{
  ExamApplication application = new ExamApplication(professor, subject, student);
  DealWith(application);
}
else
{
  DisplayWarning();
}

This function could be used to prevent exceptions. If it tells that arguments are alright, then we would proceed. On the other hand, if the function tells that arguments are not alright, then we could take alternate course of action.

All the elements are still present, just like they were with exception handling. Only this time, we are prepared for the negative scenario. This code segment is acknowledging that there are two ways to proceed and none of them is in any way exceptional.

However, this code is then indicative of another subtle issue. It turns that both Alright function and the ExamApplication constructor are now aware of the rules. On the other hand, ExamApplication constructor should not be left without protection. It must obviously check whether its arguments are fine and once again throw if they are not. That would be the clear case of code duplication, which is a very bad idea when it comes to domain rules.

We should keep rules in only one place, because that is the only way to guarantee that the same rules will be observed by all objects in the system. We can deal with code duplication by turning defensive part of this code into an object, an object which would control the rules and then construct the target object after ensuring that all rules are obeyed. In perspective, that object will replace both the Alright function, and the constructor validation in the ExamApplication class.

We shall call the new class ExamApplicationBuilder:

class ExamApplicationBuilder
{
  public ExamApplicationBuilder For(Student candidate) { ... }
  public ExamApplicationBuilder On(Subject subject) { ... }
  public ExamApplicationBuilder AdministeredBy(Professor administrator) { ... }

  public bool CanBuild() { ... }
  public ExamApplication Build() { ... }
}

It would take a lot of code to implement this builder, only to enforce that the ExamApplication object can be built if and only if the rules that are binding the three parameters are satisfied.

Major difference between the builder and the constructor is that the builder is free to expose a member, such as this CanBuild method above, and in that way indicate in advance that it might not be safe to call its Build method.

Builder pattern is probably the simplest way of externalizing rules that must be satisfied before an object comes into being. Those rules are usually referred to as existential preconditions, and they are extremely important if you want to leverage the rule which I have already quoted in this module: If you have an object, then it’s fine.

You should not have to check any subsequent rules after an object has been constructed. Those checks would be the defensive code then.

Having all the rules satisfied before the object is constructed means that you won’t have to type any of that defensive code.

Improving on the Builder Idea

From this point on, we can improve the builder further, turning it into a well-defined finite state machine. It could enforce rules one step at a time, so that the final CanBuild method is rendered unnecessary. You can learn more about this technique from the prior article Advances in Applying the Builder Design Pattern. The result of this attempt would be a fluent builder which leads to an unconditional Build method:

ExamApplicationBuilder appBuilder =
  new ExamApplicationBuilder()
    .For(student)
    .On(subject)
    .AdministeredBy(professor);

Func<ExamApplication> factory = appBuilder.Build;

This code segment doesn’t display defense from possible errors. In every step, a branching instruction must be installed to stop the process before an exception is caused. I will not show that version of code, because my plan is to supersede it with a completely different solution, in which an exception can never be thrown.

However, the benefit of this approach remains, and that is that the Builder itself is now managed by the same idea that “if you have an object, then it’s fine.” This time, if you have the builder, then you are safe to call its Build method. You can even take the delegate to its Build method and pass it on as a parameterless factory function. That is safe, because Build method will never fail when constructed this way, because all validation rules have already been checked. Please refer to the article Advances in Applying the Builder Design Pattern for more details on this idea.

Another angle of attack would be to evade the builder altogether and turn this finite state machine into a series of factory functions associated with different objects in the system. This idea is pursued in another article Substituting the Builder with the Sequence of Factory Methods. The result of this approach would again be a fluent interface, only this time fully implemented in terms of domain objects:

Exam exam = professor.Administer(subject);
ExamApplication app = student.ApplyFor(exam);

This code can still fail the same way as the one based on the builder above. And, once again, I will not show the full branching version which stops execution before each method call, because I do not believe in effectiveness of such attempts.

The original article Substituting the Builder with the Sequence of Factory Methods is solving the problem of impossible calls by introducing optional objects. You can learn more about optional objects in a separate article titled Custom Implementation of the Option/Maybe Type in C#.

Invalid calls do not produce objects, but rather produce an empty optional object. That approach would let you chain intermediate calls until final product is built, and that is the idea which entirely removes any possibility of an error. That will be the final demonstration in this article.

The Object Filtering Idea

Instead of building one object after another and stopping in case that any of the steps cannot be completed, due to invalid input, we can introduce a specific form of filters. A filter is normally applied to a sequence of objects, in order to skip some of them and leave the rest intact. For example, if we had a sequence of exam subjects, we could filter out all the subjects the given professor could not administer:

Professor professor = ...

IEnumerable<Subject> examSubjects =
  subjects.Where(professor.CanAdminister);

IEnumerable<Exam> exams =
  examSubjects.Select(subject => new Exam(subject, professor));

Instance-level Boolean method CanAdminister, defined on the Professor class, is used here as the filter. Once the subjects have been filtered, we are moving on to create the Exam objects. Well, there we are bound to call the constructor, and constructor once again brings exceptions back to the table. It may look like we are back at square one with this solution, but it still has one bright spot – the filter itself will never cause an exception!

What if we used the filter to create the valid objects, rather than just return the unchanged input objects? Here is one possible implementation of the Exam class, which pursues this idea:

class Exam
{
  public Subject Subject { get; }
  public Professor Administrator { get; }

  private Exam(
    Subject subject, Professor administrator)
  {
    this.Subject = subject;
    this.Administrator = administrator;
  }

  public static IEnumerable<Exam> TryAdminister(
    Professor administrator,
    IEnumerable<Subject> subjects) =>
    administrator is null ? Enumerable.Empty<Exam>()
    : subjects
        .Where(subject => !(subject is null))
        .Where(administrator.CanAdminister)
        .Select(subject => new Exam(administrator, subject));
}

This time, the risky call to the Exam class’s constructor is wrap inside the Exam class itself! The only constructor is made private, and therefore the outer caller cannot access it to cause damage. Instead, the only way to construct any Exam object is via the static factory function TryAdminister.

This function is the filter of a special kind. It is filtering out the objects that would otherwise cause an exception, and using the remaining objects to construct Exam instances. There is no room left for exceptions. This factory function can never throw. In case that any Subject is passed, which the specified Professor cannot administer, that Subject would simply be missing in the output. You can check for the missing objects if you are keen to know which ones have dropped, but my guess is that you would never really bother.

The static filtering function is even wrapping the null checks. In case that Professor argument is null, the result will be an empty stream. And any null contained in the sequence of subjects would also be silently removed, or filtered out. As you can see, the whole filtering method is very easy to work with. It only guarantees that valid Exam objects will appear on its output. Whichever object that didn’t make it to the output would be invalid anyway.

You can extend the same basic idea to exam applications, too.

class ExamApplication
{
  public Exam Exam { get; }
  public Student Candidate { get; }

  private ExamApplication(
    Exam exam, Student candidate)
  {
    this.Exam = exam;
    this.Candidate = candidate;
  }

  public static IEnumerable<ExamApplication> TryApplyFor(
    Student candidate, IEnumerable<Exam> exams) =>
    candidate is null ? Enumerable.Empty<ExamApplication>()
    : exams
        .Where(exam => !(exam is null))
        .Where(candidate.CanApplyFor)
        .Select(exam => new ExamApplication(exam, candidate));
}

Once again, we have reached the design which softly removes all objects that would cause a fault, implicitly producing only the valid objects. Using this system of filters may look a bit awkward, but it still hits the point – no throw statements anywhere in code:

Professor professor = ...;
Subject subject = ...;
Student candidate = ...;

IEnumerable<Exam> exams =
  Exam.TryAdminister(professor, new[] {subjects});

IEnumerable<ExamApplication> app =
  ExamApplication.TryApplyFor(candidate, exams);

On the other hand, forcing all objects into sequences only to be able to filter them out may look like an overdesign. In the article Custom Implementation of the Option/Maybe Type in C# you can learn that optional object can be viewed as a special form of a sequence, only this time a sequence with no more than one element in it. Filters can be applied to optional objects in the same way as they were applied in the code above, once again constructing either one object or no objects. Here is the final filtering solution for exams, based on optional objects, and you can refer to the original article for full explanation:

Professor professor = ...;
Subject subject = ...;
Student candidate = ...;

Option<ExamApplication> app =
  professor
    .TryAdminister(subject)
    .MapOptional(candidate.TryApplyFor);

This form is very useful and convenient in practice. You can use it to safely construct objects with complex validation without ever causing a fault. If any validation rule was violated, the result would be an empty exam application, i.e. no ExamApplication instance to work with.

Summary

In this article, we have covered a lot of ground regarding object creation.

We have started from plain constructors, concluding that the constructor is not a safe method to create objects. If constructor ever detected mismatching arguments, then it can only throw an exception. This is because constructors are not proper functions. They have no return value, and therefore they cannot indicate a result other than the created object.

From that point, we have started examining alternatives. The first alternative is to introduce a separate object responsible to check validation rules and indicate future failure even before really attempting to construct an object. However, the builder idea is only halfway there to a proper solution. Using the builder is enforcing imperative coding style, based on Boolean conditions and branching.

By the end of the demonstration, you have learned that the entire idea of using builder objects can be abandoned in favor of a special kind of filtering methods. The filter can be responsible to construct resulting objects if filter is satisfied, and to drop the object if filter is not satisfied. This wonderful idea gives rise to wholly new designs, in which failure to create an object doesn’t even exist.

At the end, you have learned that optional objects can be treated as a special, and very natural, kind of filters. You can employ optional objects to optionally construct their results – if validation rules are satisfied, then the result is constructed; otherwise an empty result is produced.

Once programmers begin to adopt the idea of filtering builders, they will stop using Boolean flags and even exceptions to indicate failed validation. Indeed, failed validation is not an exceptional event. It happens all the time, and our task as programmers is to provide a valid way out when a subsequent object cannot be constructed.


About

Zoran Horvat

Zoran Horvat is the Principal Consultant at Coding Helmet, speaker and author of 100+ articles, and independent trainer on .NET technology stack. He can often be found speaking at conferences and user groups, promoting object-oriented development style and clean coding practices and techniques that improve longevity of complex business applications.

  1. Pluralsight
  2. Udemy
  3. Twitter
  4. YouTube
  5. LinkedIn
  6. GitHub