construction

Favour Object Construction

construction

Photo by C Dustin on Unsplash

 

Should we use object construction or object initialisation? Recently I’ve touched on this question in the Improve The Code – Answered. Today I’d like to examine the dilemma in more detail. 

The title of today’s tip gives the game away. By why does construction trump initialisation? After all, most online examples use object initialisation. Let me explain with code.

Here is an example of a typical object initialiser:

  var invoiceLine = new InvoiceLine()
  {
     Product = product,
     Quantity = quantity,
     UnitPrice = product.UnitPrice,
     Subtotal = quantity * product.UnitPrice
  };

It looks fine at first glance. But there is a problem – and it’s a serious one.

Here is another instantiation of InvoiceLine:

  invoice.Add(new InvoiceLine()
  {
     Product = prod,
     Quantity = quant,
     UnitPrice = price
  });

Does this make things a bit clearer? No?

The programmer who wrote the second example forgot to calculate the Subtotal. We have a bug!

This omission could have unexpected & significant consequences: Since the Subtotal is defaulting to 0, it may result in invoices undercharging the customer. Ouch!

But it gets worse: If, as shown, the Subtotal is set via code external to the InvoiceLine class, then we could end up with several different ways of calculating the InvoiceLine.Subtotal! There are no constraints on the Subtotal calculations. For example, this InvoiceLine is possible:

  var invoiceLine = new InvoiceLine()
  {
     Product = product,
     Quantity = 3,
     UnitPrice = 1;
     Subtotal = 1000
  };

This InvoiceLine object is possible even though it is incorrect—assuming we want Subtotal equal Quantity times UnitPrice.

Object initialisation allows us to configure all settable properties as we see fit. Values are only constrained by their type and logic in the property setters!

So, how do we avoid this? 

We could calculate the Subtotal in a constructor: 

  public InvoiceLine(Product product, decimal quantity)
  {
     Product = product;
     Quantity = quantity;
     UnitPrice = product.UnitPrice;
     Subtotal = product.UnitPrice * Quantity;
  }

Note: We can further improve the code in this constructor. I’ve written about that recently here.    

To construct an InvoiceLine, we write:

var line = new InvoiceLine(product, quantity);

Isn’t that much tidier? 

We’ve hidden all initialisation in the constructor. We trust the constructor to do its job. (Or even better than trusting—we have unit tests! ;-))

Why is this better?

  • Don’t Repeat Yourself: In our InvoiceLine constructor, the Subtotal is reliably calculated in the same way every time. It is unnecessary to repeat the Subtotal calculations code each time we create a new object.
  • Safer: As we saw with our original InvoiceLine examples, when callers had to initialise the new objects, errors could creep in. On the other hand, when we construct, we can initialise the object in one go.
  • Encapsulation & Abstraction: We are putting the responsibility where it belongs, inside the object definition. Callers delegate Subtotal calculations to InvoiceLine.

In conclusion, I strongly recommend preferring object construction over object initialisation

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply