Reputation: 0
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 this line and uncomment the next, 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:
1. You have to wash hands before eating. :) - to generalize - you
can only call subs to manipulate data.There is no other choice.
2. $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
3. 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