At several places here in SO, it is claimed that having to know the exact type of an object and make a decision based on that (in an if-then-else
manner) points to a design flaw, e.g. here.
I am wondering if this is always the case. In a current small educational project (which I'm using to learn C++) I want to implement arithmetic structures, i.e. terms (such as (3+4)*x-5
) and equations (x=3*y+z
). Now, from a structural point of view, equations are very similar to terms (particularly they can all be parsed using the Shaunting-yard algorithm), so I created a base class math_struct
that both equation
and term
derive from:
class math_struct;
class term : public math_struct {...};
class equation : public math_struct {
void resolve_to(std::string subTerm);
...
};
equation
has a method resolve_to
that term does not have (since an equation can be resolved to a variable, which a term cannot), but other than that they are equal.
Now I have a method to parse a math_struct
from a string:
static std::unique_ptr<math_struct> parse_math_struct(std::string formula);
The resulting math_struct
can either be a term or an equation, depending on what the input string looks like. To know which it is, I have to do a typeid
check and perform a cast to use, if available, the resolve_to
member function.
Is this a design flaw – and how could I improve it?
I don't know if I would call it a "flaw", but I would say the design could be improved. One potential problem with checking the type to see if you can call resolve_to
is that the check could break if, for some reason, you find the need for a third class derived from math_struct
. (Someone with a mathematical background should be aware that being unable to think of a reason why does not mean there is no reason why.)
A more robust option is to add a virtual function to the base class, a simple Boolean that returns true
iff you can call resolve_to
.
An even better option might be to think about what do you do if the thing you're dealing with is not an equation.
To know which it is, I have to do a
typeid
check and perform a cast to use, if available, theresolve_to
member function.
What do you do if the resolve_to
member function is not available? Why not have a virtual function on the base class that is overridden to do that thing for term
s and overridden to call resolve_to
for equation
s? (Or that is resolve_to
for equation
s.)
There might be a situation where the best option is to "ask which exact type an object has", but I do not believe this is it. (I know there's an XML parser that relies on this technique; I have not analyzed that to see if it could be improved.)