Learning Perl Objects, References amp;amp; Modules [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Learning Perl Objects, References amp;amp; Modules [Electronic resources] - نسخه متنی

Randal L. Schwartz

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید














10.1 Nested Object Destruction


If an object holds another object
(say, as an element of an array or the value of a hash element), the
containing object is DESTROYed before any of the
contained objects begin their discarding process. This is reasonable
because the containing object may need to reference its contents in
order to be cleanly discarded. To illustrate this,
let's build a
"barn" and tear it down. And just
to be interesting, we'll make the barn a blessed
array reference, not a hash reference.

{ package Barn;
sub new { bless [ ], shift }
sub add { push @{+shift}, shift }
sub contents { @{+shift} }
sub DESTROY {
my $self = shift;
print "$self is being destroyed...\n";
for ($self->contents) {
print " ", $_->name, " goes homeless.\n";
}
}
}

Here, we're really
being minimalistic in the object definition. To create a new barn,
simply bless an empty array reference into the class name passed as
the first parameter. Adding an animal just pushes it to the back of
the barn. Asking for the barn contents merely dereferences the object
array reference to return the contents.[3]

[3] Did you
wonder why there's a plus sign (+) before
shift in two of those subroutines?
That's due to one of the quirks in
Perl's syntax. If the code were simply
@{shift}, because the curly braces contain nothing
but a bareword, it would be interpreted as a soft reference:
@{"shift"}. In Perl, the unary plus (a plus sign
at the beginning of a term) is defined to do nothing (not even
turning what follows into a number), just so it can distinguish cases
such as this.


The fun part is the destructor. Let's take the
reference to ourselves, display a debugging message about the
particular barn being destroyed, and then ask for the name of each
inhabitant in turn. In action, this would be:

my $barn = Barn->new;
$barn->add(Cow->named("Bessie"));
$barn->add(Cow->named("Gwen"));
print "Burn the barn:\n";
$barn = undef;
print "End of program.\n";

This prints:

Burn the barn:
Barn=ARRAY(0x541c) is being destroyed...
Bessie goes homeless.
Gwen goes homeless.
[Gwen has died.]
[Bessie has died.]
End of program.

Note that the barn is destroyed first, letting you get the name of
the inhabitants cleanly. However, once the barn is gone, the
inhabitants have no additional references, so they also go away, and
thus their destructors are also invoked. Compare that with the cows
having a life outside the barn:

my $barn = Barn->new;
my @cows = (Cow->named("Bessie"), Cow->named("Gwen"));
$barn->add($_) for @cows;
print "Burn the barn:\n";
$barn = undef;
print "Lose the cows:\n";
@cows = ( );
print "End of program.\n";

This produces:

Burn the barn:
Barn=ARRAY(0x541c) is being destroyed...
Bessie goes homeless.
Gwen goes homeless.
Lose the cows:
[Gwen has died.]
[Bessie has died.]
End of program.

The cows will now continue to live until the only other reference to
the cows (from the @cows array) goes away.

The references to the cows are removed only when the barn destructor
is completely finished. In some cases, you may wish instead to shoo
the cows out of the barn as you notice them. In this case,
it's as simple as destructively altering the barn
array, rather than iterating over it.[4]

[4] If
you're using a hash instead, use
delete on the elements you wish to process
immediately.


Let's alter the
Barn to Barn2 to illustrate
this:

{ package Barn2;
sub new { bless [ ], shift }
sub add { push @{+shift}, shift }
sub contents { @{+shift} }
sub DESTROY {
my $self = shift;
print "$self is being destroyed...\n";
while (@$self) {
my $homeless = shift @$self;
print " ", $homeless->name, " goes homeless.\n";
}
}
}

Now use it in the previous scenarios:

my $barn = Barn2->new;
$barn->add(Cow->named("Bessie"));
$barn->add(Cow->named("Gwen"));
print "Burn the barn:\n";
$barn = undef;
print "End of program.\n";

This produces:

Burn the barn:
Barn2=ARRAY(0x541c) is being destroyed...
Bessie goes homeless.
[Bessie has died.]
Gwen goes homeless.
[Gwen has died.]
End of program.

As you can see, Bessie had no home by being booted out of the barn
immediately, so she also died. (Poor Gwen suffers the same fate.)
There were no references to her at that moment, even before the
destructor for the barn was complete.

Thus, back to the temporary
file problem. If you have an associated temporary file for an animal,
you merely need to close it and delete the file during the
destructor:

## in Animal
use File::Temp qw(tempfile);
sub named {
my $class = shift;
my $name = shift;
my $self = { Name => $name, Color => $class->default_color };
## new code here...
my ($fh, $filename) = tempfile( );
$self->{temp_fh} = $fh;
$self->{temp_filename} = $filename;
## .. to here
bless $self, $class;
}

You now have a filehandle and its
filename stored as instance variables of the
Animal (or any class derived from
Animal). In the destructor, close it down, and
delete the file:[5]

[5] As it turns out, you can tell
File::Temp to do this automatically, but then we
wouldn't be able to illustrate doing it manually.
Doing it manually allows you to store a summary of the information
from the temporary file into a database. However,
that's too complex to show here.


sub DESTROY {
my $self = shift;
my $fh = $self->{temp_fh};
close $fh;
unlink $self->{temp_filename};
print "[", $self->name, " has died.]\n";
}

When the last reference to the
Animal-ish object is destroyed (even at the end of
the program), also automatically remove the temporary file to avoid a
mess.



/ 199