Question

I was reading on this SO page about when to check parameters when constructing an object. The accepted answer suggests throwing the exception from the constructor so that an invalid object cannot be constructed.

I agree with that approach, and I can't see how a Person can be constructed without a name.

However one of the comments suggested:

I also agree that second option is better because it is more reusable than the first one. The first option is violating Object Oriented Principles. Even the factory method is better designed than that.

Questions:

  1. Exactly what OOP principles are being violated when you throw an exception in the constructor? Is it the guideline that a constructor shouldn't do work?

  2. The other approach suggested by the OP was a PersonService, however, this is just speculation, but with this approach are you hoping that the client code will call public void addPerson(Person personToAdd) to verify that the person object doesn't contain a null? What happens if they find a way to circumvent that check? Or before public void addPerson(Person personToAdd) they use that object in another class not written by them that expects a valid Person object?

Again, this is all part of the speculation, but it seems you're putting a lot of responsibility on the client to ensure the Person object is valid when that should have been done when it was created.

Was it helpful?

Solution

1) No OOP principle is broken when you throw an exception in a constructor.

Bjarne Stroustrup recalled in his book The design and evolution of C++ how exceptions were designed into the language between 1984 and 1989 in conjunction with experts from several companies like IBM and Sun:

To some, one of the most important spect of exceptions is that they provide a general mechanism for reporting errors detected in constructors

This is true for Java and other OO languages as well.

2) Using a service to create objects is also a valid approach: It is not uncommon to use builders or factories to create objects.

Factories can allow you to manage errors differently than throwing exceptions, and especially to control the consistency of parameters before the construction.

However, it is not always desirable to use such kind of services on the top of a constructor.

And moreover it is not possible to anticipate all errors. Suppose that during the creation some unexpected event not related to the parameters happen. For example, not enough memory left to create a big object, or some resources were checked but couldn't be allocated (especially in the RAII context).

3) Nevertheless, do not abuse exceptions

Exception is not a substitute for parameter validation. The most important principle with exception is they should fire only for exceptional situations. If a bad parameter is a common situation or if you expect that one out of three construction may cause an exception, then you should really question your design, and refactor it to reduce the risk.

If you use it for defensive programming, assuming that the name should be checked before, but provide this excpetion as safety net, then it's ok.

OTHER TIPS

  1. The only principle that I can see being violated is Encapsulation, as the Exception thrown is going to expose some internal structure of the Object. If the Object is not exposed publicly, this may not be of concern. Historically, when a Object failed part way through Construction, it might be necessary to clean up any resources allocated to that point. Garbage collectors have minimized (but not eliminated) this impact. Note also that calling a Constructor explicitly couples your code to the Constructor's signature. This is largely trivial, but may require rework if the signature changes, as can happen when moving to newer versions of your dependencies.
  2. meh? This doesn't make much sense to me.

The current favored pattern (IMO) for cleanly Constructing an Object is the Builder Pattern. Among the benefits of the Builder is the ability to validate parameters before they are passed to the Constructor. This validation can happen when the particular param is set, or during the build() invocation. Validating in the param() call can act as a Circuit Breaker, failing fast and potentially eliminating work to obtain other param values before their use; ex.

...size(5).foo(getMassivelyComplexObject()).build

If 5 isn't a valid size, and the validation is implemented in the size() call, you've avoided the work required to obtain the value for foo().

In the case where a Constructor's signature changes, Builders insulate you in two ways:

  1. Builder param() calls are usually order independent.
  2. Individual Builder param() calls are optional if the Builder supplies default values for the particular param.

The Builder Pattern has lots of other goodness as well, but your question is about Failure during Construction, so I'll stop here.

Licensed under: CC-BY-SA with attribution
scroll top