What’s a good existing class/design pattern for multi-stage construction/initialization of an object in C++?
I have a class with some data members which should be initialized in different points in the program’s flow, so their initialization has to be delayed. For example one argument can be read from a file and another from the network.
Currently I am using boost::optional for the delayed construction of the data members, but it’s bothering me that optional is semantically different than delay-constructed.
What I need reminds features of boost::bind and lambda partial function application, and using these libraries I can probably design multi-stage construction – but I prefer using existing, tested classes. (Or maybe there’s another multi-stage construction pattern which I am not familiar with).
The key issue is whether or not you should distinguish completely populated objects from incompletely populated objects at the type level. If you decide not to make a distinction, then just use
boost::optionalor similar as you are doing: this makes it easy to get coding quickly. OTOH you can’t get the compiler to enforce the requirement that a particular function requires a completely populated object; you need to perform run-time checking of fields each time.Parameter-group Types
If you do distinguish completely populated objects from incompletely populated objects at the type level, you can enforce the requirement that a function be passed a complete object. To do this I would suggest creating a corresponding type
XParamsfor each relevant typeX.XParamshasboost::optionalmembers and setter functions for each parameter that can be set after initial construction. Then you can forceXto have only one (non-copy) constructor, that takes anXParamsas its sole argument and checks that each necessary parameter has been set inside thatXParamsobject. (Not sure if this pattern has a name — anybody like to edit this to fill us in?)“Partial Object” Types
This works wonderfully if you don’t really have to do anything with the object before it is completely populated (perhaps other than trivial stuff like get the field values back). If you do have to sometimes treat an incompletely populated
Xlike a “full”X, you can instead makeXderive from a typeXPartial, which contains all the logic, plusprotectedvirtual methods for performing precondition tests that test whether all necessary fields are populated. Then ifXensures that it can only ever be constructed in a completely-populated state, it can override those protected methods with trivial checks that always returntrue:Although it might seem the inheritance here is “back to front”, doing it this way means that an
Xcan safely be supplied anywhere anXPartial&is asked for, so this approach obeys the Liskov Substitution Principle. This means that a function can use a parameter type ofX&to indicate it needs a completeXobject, orXPartial&to indicate it can handle partially populated objects — in which case either anXPartialobject or a fullXcan be passed.Originally I had
isComplete()asprotected, but found this didn’t work sinceX‘s copy ctor and assignment operator must call this function on theirXPartial&argument, and they don’t have sufficient access. On reflection, it makes more sense to publically expose this functionality.