Favour Object Construction
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, theSubtotal
is reliably calculated in the same way every time. It is unnecessary to repeat theSubtotal
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 toInvoiceLine
.
In conclusion, I strongly recommend preferring object construction over object initialisation.
Leave a Reply
Want to join the discussion?Feel free to contribute!