Hi guys,
I was just reading about closures on perl.com and read that Tom Christiansen said that closures can be used to achieve data hiding in OO perl. I thought about it and came up with an example to do that:
A module written without closures:
package Dinner;
use strict;
use warnings;
my $self = {};
sub new {
my ($class) = @_;
bless $self, $class;
return $self;
}
sub washhands {
my ($obj) = @_;
$obj->{'handsclean'} = 1;
return 1;
}
sub eatfood {
my ($obj) = @_;
if (exists $obj->{'handsclean'}) {
print "\nYou washed your hands -eat all you want\n";
} else {
print "\nYou filthy animal!! Go wash your hands\n";
}
return 1;
}
1;
Its corresponding use:
#!/usr/bin/perl -w
use strict;
use Dinner;
my $dirtyeater = Dinner->new();
# I don't want to wash hands, I will just set the flag directly
# $dirtyeater->washhands;
$dirtyeater->{'handsclean'} = 1;
$dirtyeater->eatfood;
A Data::Dumper dump of $dirtyeater gives
$VAR1 = bless( {
'handsclean' => 1
}, 'Dinner' );
$dirtyeater can eat food if he knows what to change in the objects data structure. I know that if you are playing with the object's ds you do it at your own risk but I want to prevent the end user from changing any object ds specific variables. So I write another module called Dinnerclosure.pm:
package Dinnerclosure;
use strict;
use warnings;
sub new {
my ($class) = @_;
my $self = {};
my $selfmirage = {};
$selfmirage->{'washhands'} = sub {
washhands($self);
};
$selfmirage->{'eatfood'} = sub {
eatfood($self);
};
bless $selfmirage, $class;
return $selfmirage;
}
sub washhands {
my ($obj) = @_;
$obj->{'handsclean'} = 1;
return 1;
}
sub eatfood {
my ($obj) = @_;
if (exists $obj->{'handsclean'}) {
print "\nYou washed your hands -eat all you want\n";
} else {
print "\nYou filthy animal!! Go wash your hands\n";
}
return 1;
}
1;
The testcode:
#!/usr/bin/perl -w
use strict;
use Dinnerclosure;
my $cleaneater = Dinnerclosure->new();
# comment next line and uncomment the one after it, but it won't wash
+hands
$cleaneater->{'washhands'}->();
#$cleaneater->{'handsclean'} = 1;
$cleaneater->{'eatfood'}->();
print Dumper $cleaneater;
$dirtyeater cannot change the 'cleanhands' flag. If he does, he will be changing the $selfmirage hashref which does not decide cleanliness, $self does. And $dirtyeater cannot access $self because the object is:
$VAR1 = bless( {
'eatfood' => sub { "DUMMY" },
'washhands' => sub { "DUMMY" }
}, 'Dinnerclosure' );
Advantages:
- You have to wash hands before eating. :) - to generalize - you can only call subs to manipulate data.There is no other choice.
- $self and $selfmirage are lexically scoped within the new sub. They don't even have to be declared at top of package before any subs in case of Dinner.pm - better lexical scoping
- The end user does not see any object innards in the ds
WYDSIWYDG - What you don't see is what you don't get :)
-- Saurabh