Board index » delphi » Chameleon classes...

Chameleon classes...


2007-09-20 10:50:14 PM
delphi239
What are your thoughts on classes that act as chameleons?
Structure 1:
TVehicle = class(TObject)
end;
TCar = class(TVehicle)
fEngineRunning: Boolean;
end;
TBoat = class(TVehicle)
fBildgePumpRunning: Boolean;
end;
Structure 2:
TVehicle = class(TObject)
fHasEngine: Boolean;
fEngineRunning: Boolean;
fHasBildgePump: Boolean;
fBildgePumpRunning: Boolean;
end;
I'm not looking for examples of other structures, but am wondering what
everyone thinks about using chameleon classes (Structure 2). I think
Structure 1 is the proper way to express the situation. While Structure 2
will work, it colapses classes down into one class that can act as different
classes. If it is a TBoat then fHasEngine and fEngineRunning is ignored.
Also, you must first check fHasBildgePump to know if it is a boat. Then
check fBildgePumpRunning to find out if the pump is running.
 
 

Re:Chameleon classes...

In a word, I think it is "pants". In your example there are a few members,
but what happens when you start needing multiple methods to implement the
class? More importantly, what happens when you discover you need
IsRunning; virtual;
Start; virtual;
Stop; virtual;
and then have to have Boat/Car behave differently whenever Start/Stop are
called? Eyuwww, {*word*193} mess :-)
Pete
 

Re:Chameleon classes...

On Thu, 20 Sep 2007 10:50:14 -0400, Rich writes:
Quote
What are your thoughts on classes that act as chameleons?
I would use interfaces to separate out functionality. For example not
all boats will have engines.
--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com
 

Re:Chameleon classes...

Rich writes:
Quote
What are your thoughts on classes that act as chameleons?

Structure 1:

TVehicle = class(TObject)
end;

TCar = class(TVehicle)
fEngineRunning: Boolean;
end;

TBoat = class(TVehicle)
fBildgePumpRunning: Boolean;
end;

I
think Structure 1 is the proper way to express the situation.
Agreed.
Quote
While
Structure 2 will work, it colapses classes down into one class that
can act as different classes. If it is a TBoat then fHasEngine and
fEngineRunning is ignored. Also, you must first check fHasBildgePump
to know if it is a boat. Then check fBildgePumpRunning to find out if
the pump is running.
A class that tries to be everything is simply another form of spaghetti code
with all the problems that brings.
Inheritance is a proper choice here, however interfaces can also work and
have some advantages. For example, while the name escapes me, there were
some production amphibian vehicles made in the early or mide '60s, and many
of them have been restored and are still in use. They are literally both a
car and a boat. This can be solved better with interfaces than with
inheritance:
ICar = interface
property EngineRunning: Boolean;
end;
IBoat = interface
property BildgePumpRunning: Boolean;
end;
TVehicle = class(TObject)
end;
TCar = class(TVehicle, ICar)
end;
TBoat = class(TVehicle, IBoat)
end;
TAmphibian = class(TVehicle, ICar, IBoat)
end;
--
Wayne Niddery - Winwright, Inc (www.winwright.ca)
"We've all heard that a million monkeys banging on a million
typewriters will eventually reproduce the entire works of Shakespeare.
Now, thanks to the Internet, we know this is not true." - Robert
Wilensky
 

Re:Chameleon classes...

"Peter Morris" <XXXX@XXXXX.COM>writes
Quote
In a word, I think it is "pants". In your example there are a few
members, but what happens when you start needing multiple methods to
implement the class? More importantly, what happens when you discover you
need

IsRunning; virtual;
Start; virtual;
Stop; virtual;

and then have to have Boat/Car behave differently whenever Start/Stop are
called? Eyuwww, {*word*193} mess :-)
So, I have two structures and it appears you are picking Structure 1 (the
inheritence structure) as the better approach? Thus, saying chameleon
classes tend to create a "Eyuwww, {*word*193} mess"?
 

Re:Chameleon classes...

"Wayne Niddery [TeamB]" <XXXX@XXXXX.COM>writes
Quote
Inheritance is a proper choice here, however interfaces can also work and
have some advantages. For example, while the name escapes me, there were
some production amphibian vehicles made in the early or mide '60s, and
many of them have been restored and are still in use. They are literally
both a car and a boat. This can be solved better with interfaces than with
inheritance:

ICar = interface
property EngineRunning: Boolean;
end;

IBoat = interface
property BildgePumpRunning: Boolean;
end;

TVehicle = class(TObject)
end;


TCar = class(TVehicle, ICar)
end;

TBoat = class(TVehicle, IBoat)
end;

TAmphibian = class(TVehicle, ICar, IBoat)
end;

So then, multiple-inheritance implemented through interfaces?
 

Re:Chameleon classes...

"Marc Rohloff [TeamB]" <XXXX@XXXXX.COM>writes
Quote
On Thu, 20 Sep 2007 10:50:14 -0400, Rich writes:

>What are your thoughts on classes that act as chameleons?

I would use interfaces to separate out functionality. For example not
all boats will have engines.

Thx
 

Re:Chameleon classes...

Quote
So, I have two structures and it appears you are picking Structure 1
Yes.
Quote
Thus, saying chameleon classes tend to create a "Eyuwww, {*word*193} mess"?
Well, you'd be correct at least in saying your example is a "{*word*193} mess".
There might be a scenario that warrants this approach that I cannot think
of.
 

Re:Chameleon classes...

DUKW
If you are Welsh they are pronounced "Duckyoo" :-)
 

Re:Chameleon classes...

"Peter Morris" <XXXX@XXXXX.COM>writes
Quote
>Thus, saying chameleon classes tend to create a "Eyuwww, {*word*193} mess"?

Well, you'd be correct at least in saying your example is a "{*word*193} mess".
There might be a scenario that warrants this approach that I cannot think
of.
Hmm... OK.
 

Re:Chameleon classes...

"Rich" wrote
Quote
What are your thoughts on classes that act as chameleons?
As Peter and Wayne have both indicated, option 2 would normally be
considered 'unattractive'--I'd say a TCar having a pointless fHasBilgePump
property would be a definite code smell.
One of the reasons for that is demonstrated in your own discussion: "Also,
you must first check fHasBildgePump to know if it is a boat." Not all boats
have bilge pumps, but the opportunities to make bad assumptions in a super
be-anything class like that are legion. You have to keep figuring out what
you are, and you're going to get it wrong.
But Peter was exactly right in saying "There might be a scenario that
warrants this approach that I cannot think of." There's no such thing as
correct design, only appropriate design. Everything depends on the
requirements.
You mentioned two general design approaches: a flat all-case class or
inheritance. Marc added interfaces as an alternative to either. Let me add
two more:
Composition:
TPowerVehicle = TVehicle
fMainBody : TFrame;
fPowerTrain : TPowerTrain;
fManeuverController : TStreeringSystem;
fPassengerComportment : TPassengerCompartment;
end;
This might be a car or a boat--depending on whether the power train supplied
(passed to the constructor?) was wheel or propeller based. But since every
subsystem is specialized, you don't end up with a lot of case switching--any
particular system is going to be very specifically what it is. A class like
this is normally built by a factory that understands the rules and recipes
for constructing different cases.
Another approach to Interfaces: This approach really depends on how often
you actually need to treat something as a TVehicle:
IVehicle = interface;
TCar = class(TObject, IVehicle)
TBoat = class(TObject, IVehicle)
What this implies is a system in which cars and boats are not really
interchangeable at all, but there is some peculiar case that we have to
solve for both. For example
IVehicle = interface
GetRegistration
SetRegistration
end;
Being subject to state licensing may not be enough of a commonality to
justify descending them from a common class, so we just let them each
develop completely independently and handle the few outlying shared concerns
by interface rather than a common inheritance.
bobD
 

Re:Chameleon classes...

Rich writes:
Quote
So then, multiple-inheritance implemented through interfaces?
I wouldn't call that multiple inheritance, since that usually means
multiple *implementation* inheritance, i.e. you marry implementations
to avoid recoding. The fact that MI also gives your combined class
multiple interfaces (the ones of the base classes) doesn't mean that
having multiple interfaces is multiple inheritance.
--
Rudy Velthuis [TeamB]
"Ever notice when you blow in a dog's face he gets mad at you,
but when you take him in a car he sticks his head out the
window?" -- George Carlin
 

Re:Chameleon classes...

Rich writes:
Quote
>
>TAmphibian = class(TVehicle, ICar, IBoat)
>end;

So then, multiple-inheritance implemented through interfaces?
Technically it is not MI. It avoids the problems of MI that can occur by
trying to literally mash two(+) class implementations together.
It also gives more flexibility. In the above example, TAmphibian can
directly implement the required methods for both interfaces, or it can
indirectly implement them by internally creating and using classes that
implement those interfaces - which means TAmphibian could dynamically be
provided with different implementations of either interface, as well as
those implementations being usable by other classes.
--
Wayne Niddery - Winwright, Inc (www.winwright.ca)
"In a tornado, even turkeys can fly." - unknown
 

Re:Chameleon classes...

I would call it Polymorphisme, and not Multiple Inheritance
Bas Schouten
S&G and Partners BV
Quote
"Wayne Niddery [TeamB]" <XXXX@XXXXX.COM>writes
news:46f296eb$XXXX@XXXXX.COM...

>Inheritance is a proper choice here, however interfaces can also work
>and have some advantages. For example, while the name escapes me,
>there were some production amphibian vehicles made in the early or
>mide '60s, and many of them have been restored and are still in use.
>They are literally both a car and a boat. This can be solved better
>with interfaces than with inheritance:
>
>ICar = interface
>property EngineRunning: Boolean;
>end;
>IBoat = interface
>property BildgePumpRunning: Boolean;
>end;
>TVehicle = class(TObject)
>end;
>TCar = class(TVehicle, ICar)
>end;
>TBoat = class(TVehicle, IBoat)
>end;
>TAmphibian = class(TVehicle, ICar, IBoat)
>end;
So then, multiple-inheritance implemented through interfaces?

 

Re:Chameleon classes...

"Bas Schouten" wrote
Quote
I would call it Polymorphisme, and not Multiple Inheritance
Not really, in that the examples shown demonstrate adding different methods
to different descendents.
Quote
>>ICar = interface
>>property EngineRunning: Boolean;
>>end;
>>IBoat = interface
>>property BildgePumpRunning: Boolean;
>>end;
That's specialization, but not polymorphism. Polymorphism would be having a
method of TVehicle implemented differently by different descendents, so that
StartEngine on a boat starts the outboard motor
StartEngine on a jet starts the turbines
StartEngine on a nuclear sub withdraws damping rods from the reactor
StartEngine on a Trireme alerts the cadence drummer and orders the slaves to
run out the oars
The key idea being that the interface/method call is exactly the same across
all descendents, so the programmer doesn't need to know what descendent he's
working with. IBut if you're dealing with multiple different descendent
interfaces, like ICar and IBoat, then you need to know what interface you're
dealing with, so that is not polymorphism.
There's a good definition of polymorphism here
www.answers.com/topic/polymorphism-in-object-oriented-programming
bobD