Tuesday, July 03, 2007

Better Code Through Destruction

Perl's garbage collector counts references. When the count reaches zero (which means that no one has a reference), Perl reclaims the entity. The approach is simple and effective. However, circular references (when object A has a reference to object B, and object B has a reference to object A) present a problem.

A common example is a tree-like data structure. To navigate both directions--from root to leaves and vice versa--a parent node has a list of children and a child node has a reference to its parent. Many CPAN modules implement their data models this way, including HTML::Tree, XML::DOM, and Text::PDF::File. All these modules provide a method to release the memory. The client application must call the method when it no longer needs an object. However, the requirement of an explicit call is not very appealing and can result in unsafe code.

Solution:

Instead of explicitly calling the method to delete the object, create a special guard object (of another class) whose sole responsibility is to release the resource. When the guard object gets destroyed, its destructor deletes the tree.
    use HTML::TreeBuilder;

foreach my $filename (@ARGV) {
my $tree = HTML::TreeBuilder->new;
$tree->parse_file($filename);

my $sentry = Sentry->new($tree);

next unless $tree->look_down('_tag', 'img');
## next, last or return are safe here.
## Tree will be deleted automatically.
}

package Sentry;
sub new {
my $class = shift;
my $tree = shift;
return bless {tree => $tree}, $class;
}
sub DESTROY {
my $self = shift;
$self->{tree}->delete;
}

Note that now there is no need to call $tree->delete explicitly at the end of the loop. The magic is simple. The code of DESTROY method of the Sentry package calls, in turn, the method delete of the $tree object.

Finally, there is no need to code your own Sentry class. Use Object::Destroyer, originally written by Adam Kennedy.

Source: http://www.perl.com/pub/a/2007/06/07/better-code-through-destruction.html