[% setvar title Objects : Hierarchical calls to initializers and destructors %]
Note: these documents may be out of date. Do not use as reference! |
To see what is currently happening visit http://www.perl6.org/
Objects : Hierarchical calls to initializers and destructors
Maintainer: Damian Conway <damian@conway.org> Date: 1 Sep 2000 Last Modified: 18 Sep 2000 Mailing List: perl6-language-objects@perl.org Number: 189 Version: 3 Status: Frozen
This RFC proposes a new special method called BUILD
that is
invoked automagically whenever an object is created. Furthermore,
it proposes that both BUILD
and DESTROY
methods should
be invoked hierarchically in all base classes.
One of the major limitations of object-oriented Perl is that, unlike most other OO languages, it does not automatically invoke the initializers and destructors of base classes, when initializing or destructing an object of a derived class.
This leads to tediously complex code in constructors and destructors in order to manually achieve the same effect. More often, it leads to bugs.
It is proposed that Perl 6 introduce an automatic object initialization
mechanism, analogous to the automatic object clean-up mechanism
provided by DESTROY
methods.
It is further proposed that both the initialization and destruction mechanisms automatically call their corresponding base class versions to ensure that complete initialization and destruction of derived objects occurs correctly.
BUILD
methodIt is proposed that, if a class has a method named BUILD
, that method
will be invoked automatically during any call to bless
. It
is further proposed that bless
be extended to take an optional argument
list after its second argument, and that this list would be passed to
any BUILD
method invoked by the bless
.
The typical constructor would then be reduced to:
package MyClass; sub new { bless {}, @_ }
with initialization handled in a separate BUILD
routine:
sub BUILD { my ($self, @ctor_data) = @_; # initialization of object referred to by $self occurs here }
BUILD
callsIt is proposed that when an object is blessed, all of the BUILD
methods
in any of its base classes are also called, and passed the argument list
appended to the invocation of bless
. BUILD
methods would be called
in depth-first, left-most order (i.e. ancestral BUILD
methods would be
called before derived ones). Any given BUILD
method would only be called
once for the same object, no matter how many separate paths its class might be
inherited through.
For example, given the following class hierarchy:
package Base1; sub new { bless {}, @_ } sub BUILD { print "Base1::BUILD : @_\n" } package Base2; sub BUILD { print "Base2::BUILD : @_\n" } package Base3; sub BUILD { print "Base3::BUILD : @_\n" } package Derived1; use base qw(Base1 Base2); sub BUILD { print "Derived1::BUILD : @_\n" } package Derived2; use base qw(Base2 Base3); sub BUILD { print "Derived2::BUILD : @_\n" } package Rederived1; use base qw(Derived1 Derived2); sub BUILD { print "Rederived1::BUILD : @_\n" }
then the call to:
$obj = Rederived->new(1..3)
would print:
Base1::BUILD : 1 2 3 Base2::BUILD : 1 2 3 Derived1::BUILD : 1 2 3 Base3::BUILD : 1 2 3 Derived2::BUILD : 1 2 3 Rederived1::BUILD : 1 2 3
Note in particular that Base2::BUILD
is only called once (as early as
possible), even though class Rederived inherits it through two distinct paths.
DESTROY
callsIt is further proposed that when an object's destructor is invoked, all
inherited destructors would also be invoked, in depth-last, right-most
order. Again, each DESTROY
for an object would be called exactly once,
regardless of how many different paths it is inherited through.
For example, given the following class hierarchy (with the same topology
as the example for BUILD
above):
package Base1; sub new { bless {}, @_ } sub DESTROY { print "Base1::DESTROY\n" } package Base2; sub DESTROY { print "Base2::DESTROY\n" } package Base3; sub DESTROY { print "Base3::DESTROY\n" } package Derived1; use base qw(Base1 Base2); sub DESTROY { print "Derived1::DESTROY\n" } package Derived2; use base qw(Base2 Base3); sub DESTROY { print "Derived2::DESTROY\n" } package Rederived1; use base qw(Derived1 Derived2); sub DESTROY { print "Rederived1::DESTROY\n" }
then the destruction of an object:
$obj = "something else";
would print:
Rederived1::DESTROY Derived2::DESTROY Base3::DESTROY Derived1::DESTROY Base2::DESTROY Base1::DESTROY
Note that Base2::DESTROY
is only called once (as late as possible),
even though class Rederived inherits it through two distinct paths.
Note too that the DESTROY
invocation sequence is the exact reverse
of the sequence for BUILD
.
REBUILD
methodWhen a blessed object is reblessed into a different class, the REBUILD
methods of the new class (and its ancestors) would be called, rather than
it's BUILD
methods. The REBUILD
s would be called in the
same "ancestor-first" sequence as for BUILD
.
As well as any arguments passed to the bless
, REBUILD
would also be
passed -- as its second argument -- the name of the previous package,
so it could choose to invoke any cleanup methods from that package if
it so desired. For example:
# Full "destroy/create transfer" on reblessing... sub REBUILD { my ($self, $oldclass, @blessargs) = @_; $cleanup = "${oldclass}::DESTROY"; $self->$cleanup(); $self->BUILD(@blessargs); }
versus:
# Simple "clear private data" on reblessing sub REBUILD { my ($self, $oldclass, @blessargs) = @_; @{$self}{grep /^_/ keys @$self} = (); }
UNIVERSAL::new
Given that most Perl classes are hash-based and that the BUILD
method
cleanly separate construction and initialization, it might be desirable to
have the UNIVERSAL class always supply a default constructor:
package UNIVERSAL; sub new { bless {}, @_ }
If this were provided, many classes could simply define an appropriate
BUILD
method and simply inherit their constructor.
During discussions on this RFC, some people indicated a preference for
BLESS
/REBLESS
or NEW
/RENEW
, rather than BUILD
/REBUILD
.
Changes to the behaviour of DESTROY might have serious impact on code that overrides destructors in derived classes, especially if the overridden destructor delegates explicitly to its base class counterpart.
Not difficult.
perltoot
Conway, Object Oriented Perl, pp. 171-178.