Re: Why RTTI is considered as a bad design?
On Wednesday, August 22, 2012 8:28:55 PM UTC+3, Noah Roberts wrote:
On Wednesday, August 22, 2012 2:38:17 AM UTC-7, =D6=F6 Tiib wrote:
On Tuesday, August 21, 2012 4:57:06 PM UTC+3, Scott Lurndal wrote:
=?ISO-8859-1?Q?=D6=F6_Tiib?= <ootiib@hot.ee> writes:
Usually i see dynamic_cast<>() more used for cross-casting rather th=
an down=
-casting. I bring an example. A specific Door passed to SomeDoer::op=
en(Door=
*) may have Lockable interface or not. To have all Doors Lockable by=
defaul=
t would deviate from sane design since most doors can not be locked.=
Succes=
s of unlocking may depend of availability of Keys or LockPicks or wh=
atever =
for this SomeDoer so open() forwards these problems to SomeDoer::un=
lock(Lo=
ckable*):
bool SomeDoer::open( Door* door ) const
{
// we might need to unlock first on some cases
Lockable* lock = dynamic_cast<Lockable*>(door);
if ( lock != NULL && lock->isLocked() && !unlock( lock ) )
{
return false;
}
return door->open();
}
Can you tell how such a situation can be resolved as elegantly witho=
ut RTTI=
(or some self-made substitution of RTTI)?
The ->unlock() method becomes a no-op on a non-lockable door. In oth=
er words,
one may always call unlock, and if the door is lockable, it will be u=
nlocked;
if not, no-op.
Unlocking itself (with no parameters whatsoever)? It can no way made a =
responsibility of a door. If there is lock then it is real and unlocking it=
may fail without proper keys or pass-codes. A generic door knows (and shou=
ld know) nothing of it.
Only the cross-casting into "Lockable" may be (and often is) made respo=
nsibility of a "Door" but bloating its interface with fake complexities of =
possibly "Lockable" is bad design.
Actually, it seems to me that composition is better here. A door either =
has a lock or doesn't. That lock is either engaged or it isn't. Opening a=
door is either successful or it isn't (and being locked isn't the only rea=
son it might fail--could be a busted knob or something in the way). A door=
that does not have a lock could have one added to it later in many differe=
nt ways.
Yes. Refactoring inheritance into composition is the next step if needed. T=
hat happens when there is a need to change lockability dynamically during d=
oors lifetime. Not reasonable before, because it adds some data to all Door=
s (a pointer to Lock). It is easy to refactor if the cross-casting into "Lo=
ckable" is done by some "Door::getLock()" because user of the interface doe=
s not care if it got interface to the same object or its component.
Thus cross casting seems like a bad solution and instead there should be =
a failable function to attempt an open, a function that gets the lock or li=
st of locks (could be many), and then the client can look for locks, check =
their state, and open the door. No RTTI required. This seems to me to be =
the most logical solution given the problem domain.
List of locks is again the next step of refactoring if it is needed. What i=
am saying is that it is clearly over-engineering to make all doors with li=
st of locks when it is required that only few of them are non-dynamically l=
ockable with a single key. The need for additional flexibility may never ha=
ppen but the additional complexity will be there forever.