Re: Why RTTI is considered as a bad design?
On Tuesday, August 21, 2012 1:11:06 AM UTC+2, =D6=F6 Tiib wrote:
On Monday, August 6, 2012 9:43:24 PM UTC+3, Noah Roberts wrote: > On Wedn=
esday, August 1, 2012 1:23:58 AM UTC-7, (unknown) wrote: > > > Stroustrup h=
as written in his book TC++PL that the most common case of usage RTTI techn=
ique is with switch instruction, when one want to decide what code should b=
e executed basing on what is a "real" type of a passed object. There is giv=
en an example in which an object of shape class was passed to the function =
and different actions were executed depending on that whether a shape is ci=
rcle, square, triangle, etc. He has written that such construction is a sig=
n that one should replace the switch-case sequence by virtual functions. > =
See Liskov Substitution Principle: http://en.wikipedia.org/wiki/Liskov_su=
bstitution_principle > > > If your code looks like so: > > > if (type_1 * p=
tr = dynamic_cast<type_1*>(somePtr)) > ... > else if (type_2 * ptr = dy=
namic_cast<type_2*>(somePtr)) > ... > > You're violating the principle. Wha=
t this means is that all places that look like this will have to be searche=
d for, found, and modified if you add a new type behind the abstraction. Th=
is can quite rapidly explode into a maintenance nightmare and cause bugs th=
at are difficult and/or impossible to find. > Usually i see dynamic_cast<>(=
) more used for cross-casting rather than down-casting. I bring an example.=
A specific Door passed to SomeDoer::open(Door*) may have Lockable interfac=
e or not. To have all Doors Lockable by default would deviate from sane des=
ign since most doors can not be locked. Success of unlocking may depend of =
availability of Keys or LockPicks or whatever for this SomeDoer so open() f=
orwards these problems to SomeDoer::unlock(Lockable*): bool SomeDoer::open(=
Door* door ) const { // we might need to unlock first on some cases Lockab=
le* lock = dynamic_cast<Lockable*>(door); if ( lock != NULL && lock->is=
Locked() && !unlock( lock ) ) { return false; } return door->open(); } Can =
you tell how such a situation can be resolved as elegantly without RTTI (or=
some self-made substitution of RTTI)?
Anything in software can be "improved" (for some measure of the word), with=
another level of abstraction. In this case, you're handling a case of a do=
or being lockable. But if you introduce another level of abstraction, you m=
ight say "well, I don't care if this door is lockable, or there is a securi=
ty check that must be done before opening, or you have to un-jam the door b=
efore opening, or..., I only care about performing an action before opening=
the door". IF you look at things this way, you might end up with e.g.
void open_door_ex(door& d, door_pre_opener& preopen)
{
preopen(d);
door.open();
}
.... where preopen is created "with" the door (and a "no-op", or null, preop=
ener is used with a "normal" door).
And you don't need RTTI anymore (albeit, here, at the expense of carrying m=
ore context around).
Is the above a better solution than the "lockable" interface? I say, only t=
ime will tell. If there is another "preopen" action at some point, perhaps =
an abstraction is in order. If not, no, not really.
( NB: I don't like seeing a pointer parameter that's not checked for null, =
as well as success/fail returns from "action" methods like door::open :-) )
Goran.