How to run _init for all ancestor classes in diamond inheritance

Discussion in 'Perl Misc' started by Amir Karger, Dec 9, 2003.

  1. Amir Karger

    Amir Karger Guest

    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


    =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;
    }
    #######################
     
    Amir Karger, Dec 9, 2003
    #1
    1. Advertising

  2. 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
    >
    >
    >=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$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
     
    Tassilo v. Parseval, Dec 9, 2003
    #2
    1. Advertising

  3. 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.
     
    Darin McBride, Dec 9, 2003
    #3
  4. 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
    >
    >
    >=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$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
     
    Tassilo v. Parseval, Dec 9, 2003
    #4
  5. 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
    >
    >
    >=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$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
     
    Tassilo v. Parseval, Dec 9, 2003
    #5
  6. Amir Karger

    Uri Guttman Guest

    Re: How to run _init for all ancestor classes in diamondinheritance

    >>>>> "TvP" == Tassilo v Parseval <> 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 ------ -------- http://www.stemsystems.com
    --Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
    Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
     
    Uri Guttman, Dec 10, 2003
    #6
  7. Also sprach Uri Guttman:

    >>>>>> "TvP" == Tassilo v Parseval <> 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$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
     
    Tassilo v. Parseval, Dec 10, 2003
    #7
  8. Amir Karger

    Amir Karger Guest

    "Tassilo v. Parseval" <> wrote in message news:<br5gvv$pai$-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
     
    Amir Karger, Dec 10, 2003
    #8
  9. Amir Karger

    Malte Ubl Guest

    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
     
    Malte Ubl, Dec 10, 2003
    #9
  10. Also sprach Amir Karger:

    > "Tassilo v. Parseval" <> wrote in message news:<br5gvv$pai$-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$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
     
    Tassilo v. Parseval, Dec 10, 2003
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alexander Stippler

    virtual inheritance / dreaded diamond problem

    Alexander Stippler, Jul 14, 2003, in forum: C++
    Replies:
    0
    Views:
    1,878
    Alexander Stippler
    Jul 14, 2003
  2. Alexander Stippler

    virtual inheritance / dreaded diamond again

    Alexander Stippler, Aug 26, 2003, in forum: C++
    Replies:
    1
    Views:
    390
    Ron Natalie
    Aug 26, 2003
  3. Tom
    Replies:
    3
    Views:
    502
  4. John Perks and Sarah Mount

    MRO problems with diamond inheritance?

    John Perks and Sarah Mount, May 1, 2005, in forum: Python
    Replies:
    13
    Views:
    600
    Michele Simionato
    May 3, 2005
  5. Alex Hunsley
    Replies:
    4
    Views:
    368
    Colin J. Williams
    Nov 2, 2005
Loading...

Share This Page