I'm struggling a bit with the following situation: Lets say I have a class "Base" which requires some information in order to work properly. Therefore, I've set up a single constructor with the appropriate parameters, e.g.:
class Base {
public: Base(X x, Y y, Z z);
};
This works fine for general "bases". But I'd like to have subclasses that represent specific "bases" which are often used and which contain all information required to setup the base class, e.g.:
class Derived {
public: Derived() {
X x;
x.addSomeInformation("foo bar");
// ...
// now what?
}
};
Problem here is that I cannot invoke the parent constructor via the initializer list (as one would usually do) becaues the parameters to be handed over do not yet exist.
Due to this, I feel that something is seriously wrong with my design. I can make this work by introducing a protected default constructor and a "configure" method that imitates the normal constructor, but I'm not sure if this is a good idea (mainly because this way I cannot ensure that the subclass actually invokes "configure" - and if it doesn't that would leave me with a not properly initialized base class):
class Base {
public: Base(X x, Y y, Z z) { configure(x, y, z); }
protected: Base() {}
protected: void configure(X x, Y y, Z z);
};
class Derived {
public: Derived() {
X x;
// ...
configure(x, y, z);
}
};
How would one deal with such a situation in a proper way?
My first suggestion would be to make X
have a valuable constructor rather than forcing it to be default-constructed:
class Derived {
public: Derived() : Base(X("foo bar"), <blah>) {
}
};
If that's not an option, split the X-creation logic into another function and use that:
class Derived {
public: Derived() : Base(make_X(), <blah>) {
}
private: static X make_X() {
X x;
x.addSomeInformation("foo bar");
// Anything else needed.
return x;
}
};
The whole configure
(sometimes called initialize
) method concept does seem like the way to go. I've actually hit this problem myself in the past as part of a game development project, and I simply used a solution similar to what you came up with, without having it come back to bite me in the future.
As long as this class isn't part of some public API (and even if it is, really), you shouldn't worry about problems that might occur if somebody doesn't call configure
. You can break a lot of APIs if you really want to by just setting parameters to invalid values left and right, but that doesn't mean those APIs are bad. Just make sure to document this usage pattern clearly.
You're feeling uneasy because, indeed, people regard code whose correctness relies on a certain method having to call a certain other method at a certain time as being bad. And they're right, as long as this impacts the code's readability (and perhaps even performance), which in your case is simply not true. Objects that have an initialize
(or whatever you want to call it) method, alongside a constructor are not bad practice, and using such a method is actually a widely-used idiom in programming.
Another thing that would definitely help is designing the base class to fail as soon as possible whenever X/Y/Z are bad. This way, any bugs that might pop up from forgetting to call configure
can get caught and fixed before they cause any bigger problems.
An alternative would be re-thinking (if possible) the way the X
, Y
etc. objects are built, and give the user of those classes (you, in this case) more possibilities of initializing them via constructors. Dividing these objects into sub-components (if it applies in your case) could also help.
It's an answer in a very different way, but you could examine composition over inheritance.
i.e. Is Derived 'REALLY' a Base?
Another option would be to consider creating a Factory Method who is responsible for doing all the work needed to ultimately call the constructors with the arguments you want. (This is essentially what Mark B show's in his second example (well almost).
Alternatively, have Derived modify base.x and base.y to what they need to be and as a result of those setters being used update whatever was supposed to happen from construction of base.
It sounds like hiding the contrustors might be beneficial so you can force all construction through a factory which could always call product->config before returning the concrete product it created.