http://www.perlmonks.org?node_id=770977

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:

  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