I had this discussion with someone and it ended ambiguously.
Suppose you have a class
that needs to Parse
a file to gather some information. It can expose this information to you after the fact.
Should all this functionality go in a constructor, or is it okay to have like:
<Construct the Object>
<Call Parse>
My coworker argued that since we’re leaving the “information” to be gathered uninitialized in the constructor, with it only being valid after the constructor, that all the functionality should go in the constructor.
I argued that you don’t want to fail in the constructor and have no way of letting the outside world know. He said we should throw an exception.
This can be generalized to any arbitrary API, where it requires you call some N
number of functions in sequence to get your result. Should this work instead be done at construction?
3
Your coworker is right. Constructors should give you a fully initialized and usable object. I also understand why you don’t want parsing to happen in the constructor, because it can throw an exception. Whether a constructor is allowed to throw or not depends on the language, and whether you are acquiring resources that must be cleaned up upon failure.
This is why you see parsing happen in static methods. This allows you to expose a constructor that only creates a valid object, and the messy error handling happens before the constructor is invoked, leaving the constructor clean and free of bone-headed exceptions.
Parsing is a situation where you expect failure. That’s why it is useful to separate parsing from object initialization, and this is easy to implement with a static method.
This can be generalized to any arbitrary API, where it requires you call some N number of functions in sequence to get your result.
I don’t believe you can generalize this to any API. Static methods work great for parsing, but you want to avoid situations where order matters. Don’t force callers to guess the order in which functions should be called. There are a number of other object creational patterns you can explore, but they are very situational. Consider splitting things into their own types, where a call to one method returns a different kind of object, so it becomes impossible to call things out of sequence.
1