Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > How to run _init for all ancestor classes in diamond inheritance

Reply
Thread Tools

How to run _init for all ancestor classes in diamond inheritance

 
 
Amir Karger
Guest
Posts: n/a
 
      12-09-2003
I've got a simple diamond class hierarchy (code below). In order to
bore people, I've chosen to use Vehicle as the base class.
SolarVehicle and WheeledVehicle inherit from Vehicle, and SolarCar
inherits from both of those child classes. (But you could have a
SolarSeaPlane that's a SolarVehicle and not a WheeledVehicle.)

I want to specify a new and an _init for the base class.

new just blesses {} and calls _init.

_init should call _init for every parent class, so that you initialize
all the attributes of the object, including attributes of the parent
classes along with this class.

So how do I write Vehicle::_init such that SolarCar::_init will
inherit it and call Vehicle::_init, SolarVehicle::_init, and
WheeledVehicle::_init? (Writing SolarCar::_init so that it overloads
Vehicle::_init and calls the parent class _init's separate would be
unelegant, it seems to me.)

I've written code below that does everything except the step of
getting the base classes. I feel like my problem here isn't that Perl
can't do it, but just a syntax problem. I really just want a "foreach
(@ISA)" - but how do I write an inheritable Vehicle::_init and refer
to @ISA such that the @ISA of the package in which _init is currently
running is used, instead of always @Vehicle::ISA or @{ref($self) .
"::ISA"}.

I don't *think* this problem is due to diamond inheritance per se as
much as multiple inheritance in general. Also, I have a feeling Damian
Conway's Objects book answers this but I no longer have it. Long
story.

HELP!

-Amir Karger
http://www.velocityreviews.com/forums/(E-Mail Removed)

=cut

##################
package Vehicle;
@Vehicle::ISA = ();

sub new {
my ($class, %args) = @_;
print "new $class\n";
my $self = {};
bless $self, $class;
$self->_init(%args);
}

sub _init {
my ($self, %args) = @_;
my $class = ref $self or die "no class for $self\n";
print "_init for class $class.";
# Note: can't use $class . "::ISA", because $class will
# always be the class of the original object.
my @bases = EVERYTHING IN THIS CLASS' @ISA;
foreach my $base (@bases) {
my $c = $base . "::_init";
$self->$c(%args);
}
$self->_class_init(%args);
}

sub _class_init {
my ($self, %args) = @_;
$self->{"name"} = "Default vehicle name";
}

package WheeledVehicle;
@WheeledVehicle::ISA = qw(Vehicle);

sub _class_init {
my ($self, %args) = @_;
$self->{"wheels"} = 4;
}

package SolarVehicle;
@SolarVehicle::ISA = qw(Vehicle);

sub _class_init {
my ($self, %args) = @_;
$self->{"panels"} = 2;
}

package SolarCar;
@SolarCar::ISA = qw(WheeledVehicle SolarVehicle);

sub _class_init {
my ($self, %args) = @_;
$self->{"panels"} = 2;
}
#######################
 
Reply With Quote
 
 
 
 
Tassilo v. Parseval
Guest
Posts: n/a
 
      12-09-2003
Also sprach Amir Karger:

> I've got a simple diamond class hierarchy (code below). In order to
> bore people, I've chosen to use Vehicle as the base class.
> SolarVehicle and WheeledVehicle inherit from Vehicle, and SolarCar
> inherits from both of those child classes. (But you could have a
> SolarSeaPlane that's a SolarVehicle and not a WheeledVehicle.)
>
> I want to specify a new and an _init for the base class.
>
> new just blesses {} and calls _init.
>
> _init should call _init for every parent class, so that you initialize
> all the attributes of the object, including attributes of the parent
> classes along with this class.
>
> So how do I write Vehicle::_init such that SolarCar::_init will
> inherit it and call Vehicle::_init, SolarVehicle::_init, and
> WheeledVehicle::_init? (Writing SolarCar::_init so that it overloads
> Vehicle::_init and calls the parent class _init's separate would be
> unelegant, it seems to me.)


I don't agree here. Vehicle is the most generic of all your classes and
its _init Method should really only arrange for those things that every
vehicle has. Furthermore, Vehicle shouldn't have to bother whether it is
used as a superclass or not. Ideally, it doesn't even realize it.

> I've written code below that does everything except the step of
> getting the base classes. I feel like my problem here isn't that Perl
> can't do it, but just a syntax problem. I really just want a "foreach
> (@ISA)" - but how do I write an inheritable Vehicle::_init and refer
> to @ISA such that the @ISA of the package in which _init is currently
> running is used, instead of always @Vehicle::ISA or @{ref($self) .
> "::ISA"}.
>
> I don't *think* this problem is due to diamond inheritance per se as
> much as multiple inheritance in general. Also, I have a feeling Damian
> Conway's Objects book answers this but I no longer have it. Long
> story.
>
> HELP!
>
> -Amir Karger
> (E-Mail Removed)
>
>=cut
>
> ##################
> package Vehicle;
> @Vehicle::ISA = ();
>
> sub new {
> my ($class, %args) = @_;
> print "new $class\n";
> my $self = {};
> bless $self, $class;
> $self->_init(%args);
> }
>
> sub _init {
> my ($self, %args) = @_;
> my $class = ref $self or die "no class for $self\n";
> print "_init for class $class.";
> # Note: can't use $class . "::ISA", because $class will
> # always be the class of the original object.
> my @bases = EVERYTHING IN THIS CLASS' @ISA;
> foreach my $base (@bases) {
> my $c = $base . "::_init";
> $self->$c(%args);
> }
> $self->_class_init(%args);
> }


So trim this down to the minimum of what is required for a plain
Vehicle. Would become:

sub _init {
my ($self, %args) = @_;
$self->{name} = "Default vehicle name";
}

Your subclasses probably want to call the superclass' _init as well, I
assume. So they become:

> package WheeledVehicle;
> @WheeledVehicle::ISA = qw(Vehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"wheels"} = 4;
> }


sub _init {
my ($self, %args) = @_;
$self->SUPER::_init;
$self->{wheels} = 4;
}

> package SolarVehicle;
> @SolarVehicle::ISA = qw(Vehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"panels"} = 2;
> }


sub _init {
my ($self, %args) = @_;
$self->SUPER::_init;
$self->{panels} = 2;
}

> package SolarCar;
> @SolarCar::ISA = qw(WheeledVehicle SolarVehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"panels"} = 2;
> }


This class has more than one ancestor:

sub _class_init {
my ($self, %args) = @_;
for (@SolarCar::ISA) {
my $init = $_->can("_init")
and $self->$init;
}
$self->{panels} = 2;
}

You can use this foreach loop in every _init method actually since it
checks whether each class in @ISA has (either through inheritance or by
providing it) an _init method.

The only problem with this is that Vehicle's _init method is now called
twice because both WheelVehicle::_init and SolarVehicle::_init will call
it.

> #######################


Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus}) !JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexi ixesixeseg;y~\n~~dddd;eval
 
Reply With Quote
 
 
 
 
Darin McBride
Guest
Posts: n/a
 
      12-09-2003
Amir Karger wrote:

> I've got a simple diamond class hierarchy (code below). In order to
> bore people, I've chosen to use Vehicle as the base class.
> SolarVehicle and WheeledVehicle inherit from Vehicle, and SolarCar
> inherits from both of those child classes. (But you could have a
> SolarSeaPlane that's a SolarVehicle and not a WheeledVehicle.)
>
> I want to specify a new and an _init for the base class.
>
> new just blesses {} and calls _init.
>
> _init should call _init for every parent class, so that you initialize
> all the attributes of the object, including attributes of the parent
> classes along with this class.


Easiest way is to use NEXT (on CPAN) the same way you would use SUPER,
except that NEXT follows the ISA chain the way you want.

If you can't use NEXT directly, you may be able to steal some ideas
from it... which is what I did.

sub _init
{
my $class = shift;
my $orig_class = shift;

no strict 'refs'; # there be dragons here
# do ancestors first, allowing subclasses to "override" changes.
foreach my $super (@{"${class}::ISA"})
{
$super->_init();
}

if (
*{"${class}::class_init"}{CODE} and
ref *{"${class}::class_init"}{CODE} eq 'CODE' # should always be
true?
)
{
*{"${class}::initialise_parser"}{CODE}->();
}
}

There is probably a nicer way of saying some of this, but that's
basically the idea. Note that this doesn't take care of the diamond
shape where it may call the top-level class multiple times.
 
Reply With Quote
 
Tassilo v. Parseval
Guest
Posts: n/a
 
      12-09-2003
Also sprach Amir Karger:

> I've got a simple diamond class hierarchy (code below). In order to
> bore people, I've chosen to use Vehicle as the base class.
> SolarVehicle and WheeledVehicle inherit from Vehicle, and SolarCar
> inherits from both of those child classes. (But you could have a
> SolarSeaPlane that's a SolarVehicle and not a WheeledVehicle.)
>
> I want to specify a new and an _init for the base class.
>
> new just blesses {} and calls _init.
>
> _init should call _init for every parent class, so that you initialize
> all the attributes of the object, including attributes of the parent
> classes along with this class.
>
> So how do I write Vehicle::_init such that SolarCar::_init will
> inherit it and call Vehicle::_init, SolarVehicle::_init, and
> WheeledVehicle::_init? (Writing SolarCar::_init so that it overloads
> Vehicle::_init and calls the parent class _init's separate would be
> unelegant, it seems to me.)


I don't agree here. Vehicle is the most generic of all your classes and
its _init Method should really only arrange for those things that every
vehicle has. Furthermore, Vehicle shouldn't have to bother whether it is
used as a superclass or not. Ideally, it doesn't even realize it.

> I've written code below that does everything except the step of
> getting the base classes. I feel like my problem here isn't that Perl
> can't do it, but just a syntax problem. I really just want a "foreach
> (@ISA)" - but how do I write an inheritable Vehicle::_init and refer
> to @ISA such that the @ISA of the package in which _init is currently
> running is used, instead of always @Vehicle::ISA or @{ref($self) .
> "::ISA"}.
>
> I don't *think* this problem is due to diamond inheritance per se as
> much as multiple inheritance in general. Also, I have a feeling Damian
> Conway's Objects book answers this but I no longer have it. Long
> story.
>
> HELP!
>
> -Amir Karger
> (E-Mail Removed)
>
>=cut
>
> ##################
> package Vehicle;
> @Vehicle::ISA = ();
>
> sub new {
> my ($class, %args) = @_;
> print "new $class\n";
> my $self = {};
> bless $self, $class;
> $self->_init(%args);
> }
>
> sub _init {
> my ($self, %args) = @_;
> my $class = ref $self or die "no class for $self\n";
> print "_init for class $class.";
> # Note: can't use $class . "::ISA", because $class will
> # always be the class of the original object.
> my @bases = EVERYTHING IN THIS CLASS' @ISA;
> foreach my $base (@bases) {
> my $c = $base . "::_init";
> $self->$c(%args);
> }
> $self->_class_init(%args);
> }


So trim this down to the minimum of what is required for a plain
Vehicle. Would become:

sub _init {
my ($self, %args) = @_;
$self->{name} = "Default vehicle name";
}

Your subclasses probably want to call the superclass' _init as well, I
assume. So they become:

> package WheeledVehicle;
> @WheeledVehicle::ISA = qw(Vehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"wheels"} = 4;
> }


sub _init {
my ($self, %args) = @_;
$self->SUPER::_init;
$self->{wheels} = 4;
}

> package SolarVehicle;
> @SolarVehicle::ISA = qw(Vehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"panels"} = 2;
> }


sub _init {
my ($self, %args) = @_;
$self->SUPER::_init;
$self->{panels} = 2;
}

> package SolarCar;
> @SolarCar::ISA = qw(WheeledVehicle SolarVehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"panels"} = 2;
> }


This class has more than one ancestor:

sub _class_init {
my ($self, %args) = @_;
for (@SolarCar::ISA) {
my $init = $_->can("_init")
and $self->$init;
}
$self->{panels} = 2;
}

You can use this foreach loop in every _init method actually since it
checks whether each class in @ISA has (either through inheritance or by
providing it) an _init method.

The only problem with this is that Vehicle's _init method is now called
twice because both WheelVehicle::_init and SolarVehicle::_init will call
it.

> #######################


Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus}) !JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexi ixesixeseg;y~\n~~dddd;eval
 
Reply With Quote
 
Tassilo v. Parseval
Guest
Posts: n/a
 
      12-09-2003
Also sprach Amir Karger:

> I've got a simple diamond class hierarchy (code below). In order to
> bore people, I've chosen to use Vehicle as the base class.
> SolarVehicle and WheeledVehicle inherit from Vehicle, and SolarCar
> inherits from both of those child classes. (But you could have a
> SolarSeaPlane that's a SolarVehicle and not a WheeledVehicle.)
>
> I want to specify a new and an _init for the base class.
>
> new just blesses {} and calls _init.
>
> _init should call _init for every parent class, so that you initialize
> all the attributes of the object, including attributes of the parent
> classes along with this class.
>
> So how do I write Vehicle::_init such that SolarCar::_init will
> inherit it and call Vehicle::_init, SolarVehicle::_init, and
> WheeledVehicle::_init? (Writing SolarCar::_init so that it overloads
> Vehicle::_init and calls the parent class _init's separate would be
> unelegant, it seems to me.)


I don't agree here. Vehicle is the most generic of all your classes and
its _init Method should really only arrange for those things that every
vehicle has. Furthermore, Vehicle shouldn't have to bother whether it is
used as a superclass or not. Ideally, it doesn't even realize it.

> I've written code below that does everything except the step of
> getting the base classes. I feel like my problem here isn't that Perl
> can't do it, but just a syntax problem. I really just want a "foreach
> (@ISA)" - but how do I write an inheritable Vehicle::_init and refer
> to @ISA such that the @ISA of the package in which _init is currently
> running is used, instead of always @Vehicle::ISA or @{ref($self) .
> "::ISA"}.
>
> I don't *think* this problem is due to diamond inheritance per se as
> much as multiple inheritance in general. Also, I have a feeling Damian
> Conway's Objects book answers this but I no longer have it. Long
> story.
>
> HELP!
>
> -Amir Karger
> (E-Mail Removed)
>
>=cut
>
> ##################
> package Vehicle;
> @Vehicle::ISA = ();
>
> sub new {
> my ($class, %args) = @_;
> print "new $class\n";
> my $self = {};
> bless $self, $class;
> $self->_init(%args);
> }
>
> sub _init {
> my ($self, %args) = @_;
> my $class = ref $self or die "no class for $self\n";
> print "_init for class $class.";
> # Note: can't use $class . "::ISA", because $class will
> # always be the class of the original object.
> my @bases = EVERYTHING IN THIS CLASS' @ISA;
> foreach my $base (@bases) {
> my $c = $base . "::_init";
> $self->$c(%args);
> }
> $self->_class_init(%args);
> }


So trim this down to the minimum of what is required for a plain
Vehicle. Would become:

sub _init {
my ($self, %args) = @_;
$self->{name} = "Default vehicle name";
}

Your subclasses probably want to call the superclass' _init as well, I
assume. So they become:

> package WheeledVehicle;
> @WheeledVehicle::ISA = qw(Vehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"wheels"} = 4;
> }


sub _init {
my ($self, %args) = @_;
$self->SUPER::_init;
$self->{wheels} = 4;
}

> package SolarVehicle;
> @SolarVehicle::ISA = qw(Vehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"panels"} = 2;
> }


sub _init {
my ($self, %args) = @_;
$self->SUPER::_init;
$self->{panels} = 2;
}

> package SolarCar;
> @SolarCar::ISA = qw(WheeledVehicle SolarVehicle);
>
> sub _class_init {
> my ($self, %args) = @_;
> $self->{"panels"} = 2;
> }


This class has more than one ancestor:

sub _class_init {
my ($self, %args) = @_;
for (@SolarCar::ISA) {
my $init = $_->can("_init")
and $self->$init;
}
$self->{panels} = 2;
}

You can use this foreach loop in every _init method actually since it
checks whether each class in @ISA has (either through inheritance or by
providing it) an _init method.

The only problem with this is that Vehicle's _init method is now called
twice because both WheelVehicle::_init and SolarVehicle::_init will call
it.

> #######################


Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus}) !JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexi ixesixeseg;y~\n~~dddd;eval
 
Reply With Quote
 
Uri Guttman
Guest
Posts: n/a
 
      12-10-2003
>>>>> "TvP" == Tassilo v Parseval <(E-Mail Removed)> writes:

TvP> sub _class_init {
TvP> my ($self, %args) = @_;
TvP> for (@SolarCar::ISA) {
TvP> my $init = $_->can("_init")
TvP> and $self->$init;

i would double check if that works. there is a well known problem with
refering to a my var in the statement that declares it. the second $init
refers to a previously existing $init and is not strict safe. i would
write that as:

if (my $init = $_->can("_init") ) {
$self->$init;
}

and that is my safe and even clearer IMO.

uri

--
Uri Guttman ------ (E-Mail Removed) -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
 
Reply With Quote
 
Tassilo v. Parseval
Guest
Posts: n/a
 
      12-10-2003
Also sprach Uri Guttman:

>>>>>> "TvP" == Tassilo v Parseval <(E-Mail Removed)> writes:

>
> TvP> sub _class_init {
> TvP> my ($self, %args) = @_;
> TvP> for (@SolarCar::ISA) {
> TvP> my $init = $_->can("_init")
> TvP> and $self->$init;
>
> i would double check if that works. there is a well known problem with
> refering to a my var in the statement that declares it. the second $init
> refers to a previously existing $init and is not strict safe. i would
> write that as:
>
> if (my $init = $_->can("_init") ) {
> $self->$init;
> }
>
> and that is my safe and even clearer IMO.


You are right, sorry. I was too concerned with condensing it into one
statement for no reason.

Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus}) !JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexi ixesixeseg;y~\n~~dddd;eval
 
Reply With Quote
 
Amir Karger
Guest
Posts: n/a
 
      12-10-2003
"Tassilo v. Parseval" <(E-Mail Removed)> wrote in message news:<br5gvv$pai$(E-Mail Removed)-Aachen.DE>...
> Also sprach Amir Karger:
>
> > So how do I write Vehicle::_init such that SolarCar::_init will
> > inherit it and call Vehicle::_init, SolarVehicle::_init, and
> > WheeledVehicle::_init? (Writing SolarCar::_init so that it overloads
> > Vehicle::_init and calls the parent class _init's separate would be
> > unelegant, it seems to me.)

>
> I don't agree here. Vehicle is the most generic of all your classes and
> its _init Method should really only arrange for those things that every
> vehicle has. Furthermore, Vehicle shouldn't have to bother whether it is
> used as a superclass or not. Ideally, it doesn't even realize it.


This is obviously an OO theory question, and I am very weak on OO
theory.
How does the idea that a class should be easily inheritable interface
with the idea that a class shouldn't know it's being inherited from?
Are classes really not supposed to know they're inherited from? What
about abstract classes, which can't even instantiate objects?
Obviously they'll be inherited from!

I guess the point of your argument is that all code in the base class
should represent the base class' functionality, and a class shouldn't
just be a convenient dumping ground for functionality that happens to
be shared between all the subclasses of that class. To me, though,
this conflicts with the idea that I can write the code just once in
the base class, rather than writing a new _init in every subclass.
That makes it easier to inherit, avoids bugs in needlessly replicated
_init functions, and elegance-wise avoids redundant code. So the real
question is, is that True or False Laziness? I can see an argument
for each side. As it happens, in my application, I don't think
there'll ever be more than four classes. But what if I had thirty
classes; should I really put a new _init in each one?

Would you be happier if I did this:

package Vehicle;
# Just do stuff that applies to Vehicle AND its subclasses
sub _init {
shift->{"name"} = "default name";
}

package ClassThatInheritsFromVehicle;
@ISA = qw(Vehicle);
# Do stuff that applies only to subclasses
sub _init {
# put my Vehicle::_init here
}

and then have every subclass inherit from there instead? Now I'm
creating a class whose whole point is to foster good inheritance, so
it makes sense to put an inheritance-helping _init in there. Vehicle
now has only Vehicle-related functionality, and all the sub-Vehicle
classes just inherit from Vehicle. But is it really worth creating a
class like this just to allow inheritance? My sense of OO is that it's
OK if I centralize code for all the subclasses in a superclass, as
long as that code doesn't break the superclass. This may break OO
theory, but is it the kind of breaking that Perl approves of and
sometimes encourages?

I guess this problem is arising because I'm trying to force a non-Perl
OO model (essentially calling every ancestor class' new()) onto Perl.
Still, I would think it's the rule rather than the exception that
you'd want objects of the child classes to have the attributes of the
parent classes. (Perl 6 has Rules AND Exceptions And there are
probably many cases out there where there's more than one layer of
inheritance, and some multiple inheritance, such that you can't just
call $self->SUPER::init(). What have other folks used in this
situation?

> > I feel like my problem here isn't that Perl
> > can't do it, but just a syntax problem. I really just want a "foreach
> > (@ISA)" - but how do I write an inheritable Vehicle::_init and refer
> > to @ISA such that the @ISA of the package in which _init is currently
> > running is used, instead of always @Vehicle::ISA or @{ref($self) .
> > "::ISA"}.


As it happens, I realized I can solve this problem by passing $class
as the first argument to _init as I move up the ladder. Then I just
use
@{$class . "::ISA"}. Of course, this is pretty ugly. It's kind of
strange to me to think that if you call $foo->class::bar() then bar()
can't tell what class it was called with. caller() doesn't seem to
give me the right info either. So I'm still somewhat unsatisfied.
(Sounds like the kind of thing Perl6 would have an operator for,
though. How about <- to mean the class I was called with?)

But the rest of your email is probably the more important part.

[Generously offered code snipped]

> The only problem with this is that Vehicle's _init method is now called
> twice because both WheelVehicle::_init and SolarVehicle::_init will call
> it.


This is a very minor problem which also existed in my code: I can put
a hash in the object that stores which classes' inits have been called
on the object so far, and return if $self->{"_init"}{$class}.

-Amir
 
Reply With Quote
 
Malte Ubl
Guest
Posts: n/a
 
      12-10-2003
Amir Karger wrote:

> I've got a simple diamond class hierarchy (code below). In order to
> bore people, I've chosen to use Vehicle as the base class.
> SolarVehicle and WheeledVehicle inherit from Vehicle, and SolarCar
> inherits from both of those child classes. (But you could have a
> SolarSeaPlane that's a SolarVehicle and not a WheeledVehicle.)


Everytime you have a SolarWheeledMountableWindowedScollableCyberVehicle
there is a simple answer:

USE DELEGATION

or more specific the decorator pattern.


bye
malte


 
Reply With Quote
 
Tassilo v. Parseval
Guest
Posts: n/a
 
      12-10-2003
Also sprach Amir Karger:

> "Tassilo v. Parseval" <(E-Mail Removed)> wrote in message news:<br5gvv$pai$(E-Mail Removed)-Aachen.DE>...
>> Also sprach Amir Karger:
>>
>> > So how do I write Vehicle::_init such that SolarCar::_init will
>> > inherit it and call Vehicle::_init, SolarVehicle::_init, and
>> > WheeledVehicle::_init? (Writing SolarCar::_init so that it overloads
>> > Vehicle::_init and calls the parent class _init's separate would be
>> > unelegant, it seems to me.)

>>
>> I don't agree here. Vehicle is the most generic of all your classes and
>> its _init Method should really only arrange for those things that every
>> vehicle has. Furthermore, Vehicle shouldn't have to bother whether it is
>> used as a superclass or not. Ideally, it doesn't even realize it.

>
> This is obviously an OO theory question, and I am very weak on OO
> theory.
> How does the idea that a class should be easily inheritable interface
> with the idea that a class shouldn't know it's being inherited from?
> Are classes really not supposed to know they're inherited from? What
> about abstract classes, which can't even instantiate objects?
> Obviously they'll be inherited from!


The problem is when superclasses contain code that is really just there
to satisfy the needs of a particular subclass. This makes deriving
another class harder since this new class might not have these
requirements.

> I guess the point of your argument is that all code in the base class
> should represent the base class' functionality, and a class shouldn't
> just be a convenient dumping ground for functionality that happens to
> be shared between all the subclasses of that class. To me, though,
> this conflicts with the idea that I can write the code just once in
> the base class, rather than writing a new _init in every subclass.


Common code should go into superclasses, that's right. What I didn't
like with your approach was that the class Vehicle would look down onto
its child classes and see from which classes those are derived by
looking at their @ISA. Consider you derive a new class from SolarVehicle
that - for some reason - only wants to call Vehicle's _init method and
not that of SolarVehicle. This would be impossible in an approach where
the class highest in the hierarchy decides which init-methods to call.

> That makes it easier to inherit, avoids bugs in needlessly replicated
> _init functions, and elegance-wise avoids redundant code. So the real
> question is, is that True or False Laziness? I can see an argument
> for each side. As it happens, in my application, I don't think
> there'll ever be more than four classes. But what if I had thirty
> classes; should I really put a new _init in each one?
>
> Would you be happier if I did this:
>
> package Vehicle;
> # Just do stuff that applies to Vehicle AND its subclasses
> sub _init {
> shift->{"name"} = "default name";
> }
>
> package ClassThatInheritsFromVehicle;
> @ISA = qw(Vehicle);
> # Do stuff that applies only to subclasses
> sub _init {
> # put my Vehicle::_init here
> }
>
> and then have every subclass inherit from there instead? Now I'm
> creating a class whose whole point is to foster good inheritance, so
> it makes sense to put an inheritance-helping _init in there. Vehicle
> now has only Vehicle-related functionality, and all the sub-Vehicle
> classes just inherit from Vehicle. But is it really worth creating a
> class like this just to allow inheritance? My sense of OO is that it's
> OK if I centralize code for all the subclasses in a superclass, as
> long as that code doesn't break the superclass. This may break OO
> theory, but is it the kind of breaking that Perl approves of and
> sometimes encourages?


It's probably best to put OO theory aside when programming Perl. The
fact that you can look at your child classes' @ISA is already something
that most other object-oriented classes don't allow and would consider a
severe violation against the principles of OO. Screw that, the purity of
ideas is never something Perl cared about a lot.

> As it happens, I realized I can solve this problem by passing $class
> as the first argument to _init as I move up the ladder. Then I just
> use
> @{$class . "::ISA"}. Of course, this is pretty ugly. It's kind of
> strange to me to think that if you call $foo->class::bar() then bar()
> can't tell what class it was called with. caller() doesn't seem to
> give me the right info either. So I'm still somewhat unsatisfied.
> (Sounds like the kind of thing Perl6 would have an operator for,
> though. How about <- to mean the class I was called with?)


Are you sure that caller() wont help you? The first item of the list
returned by caller() is always the package from which the current
function was called. That should be enough.

>> The only problem with this is that Vehicle's _init method is now called
>> twice because both WheelVehicle::_init and SolarVehicle::_init will call
>> it.

>
> This is a very minor problem which also existed in my code: I can put
> a hash in the object that stores which classes' inits have been called
> on the object so far, and return if $self->{"_init"}{$class}.


Sounds too complicated. Just make sure that the _init() classes only do
things that wont break when they are done twice.

Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus}) !JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexi ixesixeseg;y~\n~~dddd;eval
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
cast classes to common ancestor deadguysfrom@gmail.com C++ 4 10-07-2007 04:13 AM
Why doesn't ri look into ancestor classes to find help? Ilmari Heikkinen Ruby 0 01-17-2005 12:29 AM
virtual inheritance / dreaded diamond again Alexander Stippler C++ 1 08-26-2003 02:53 PM
virtual inheritance / dreaded diamond problem Alexander Stippler C++ 0 07-14-2003 04:52 PM



Advertisments