The section on Encapsulation in Chapter 15 of Damian's Perl Best Practices
clearly illustrates the several advantages of inside out objects.
While I have a sense of the advantages,
I lack the knowledge/experience to even summarize them here. However, I suspect that the following twist on that version of inside-out objects
still retains the advantages while being simpler (but do not know enough to know for sure).
- Encapsulation is retained.
- I suspect the support for spell check in this version is good enough.
- Regarding memory, this version has one hash per object, but introduces one subroutine
per attribute (to support spell checking during run (not compile) time: sub attribute() returns 'attribute'). (I was asked to point out that the update done in response to a comment by chromatic moves the subroutines created to support spell check into members of the hash and so they are repeated for each instance of the object.)
-
I haven't written too many modules and so have no clue if the addition of a sub
to dump attributes is sufficient to overcome the "only serious drawback of
inside-out objects".
Update: Regarding performance, my inclination is to put the spell-check subs (sub attribute() returning 'attribute') in a BEGIN block under the assumption that the compiler will optimize away these trivial subs.
Update: Modified code to respond to comment by chromatic.
Update - Psychological Aspect:
I now understand why I had trouble using the original scheme in PBP.
While using that scheme, my mind had to deal with multiple
attributes -- there wasn't any single object for my mind to use.
The Package name represents a family of potential objects; and
the object itself doesn't provide anything to think about since it is
just a meaningless un-named scalar; so
my mind had to deal with the multitude of "attribute_of" (which overloaded the mind to the point of being almost paralyzing). In the
present scheme, my mind works with a single, meaningful hash.
{
package Spellaid; # nothing significant about this name
use warnings;
use strict;
use Scalar::Util qw(refaddr);
use Data::Dumper;
our $VERSION = '0.1.0';
my %stuff_of; # single hash per object
my @vars = qw(name task);
{
no strict qw(refs); # all the attributes
foreach my $var ( @vars )
{
=for old_1
*{$var} = sub { return $var; }; # the spell-checker!
=cut
*{'set_'.$var} = sub { ${$stuff_of{refaddr $_[0]}}{$var} = $_[1
+];};
*{'get_'.$var} = sub { return ${$stuff_of{refaddr $_[0]}}{$var}
+;}
}
}
sub new
{
my($class, @args) = @_;
my $obj = bless \do{my $anon_scalar}, $class;
my $c;
${$c}{name} = 'the name';
${$c}{task} = 'the task';
# begin added after commenting out "for old_1"
{
no strict qw(refs);
foreach my $var ( @vars )
{
${$c}{'_'.$var} = sub { return $var; };
}
}
# end added
$stuff_of{refaddr $obj} = $c;
return $obj;
}
=for old_1
sub a_complex_sub
{
my ($self, $new_name) = @_;
# deep inside this complex sub, we need the name ...
# issue is to catch mis-spelling of the hash key "name"
# print "nom: [", ${$stuff_of{refaddr $self}}{name()}, "]\n";
# next line will fail during run time (_not_ compile time)
# but does fail with a Kaboom!
# Undefined subroutine &Spellaid::nmae called at <file> <li
+ne>
# So a good/explicit Kaboom!, albeit a late one.
print "nom: [", ${$stuff_of{refaddr $self}}{nmae()}, "]\n";
}
=cut
# begin added after commenting out "for old_1"
sub a_complex_sub
{
my ($self, $new_name) = @_;
# deep inside this complex sub, we need the name ...
# the correct way would be to extract the name via:
#
# my $whatever_for_name = ${$stuff_of{refaddr $self}}{_name}();
#
# Typo of whatever_for_name will be caught at compile time,
# but we are interested in typos of the attribute "name"
# So suppose we make a typo "nmae":
my $whatever_for_name = ${$stuff_of{refaddr $self}}{_nmae}();
# the preceding line fail during run time (_not_ compile time)
# but failure message will show precisely what went wrong
# and where exactly. So we do have a good/explicit
# Kaboom!, albeit a late one.
# how a_complex_sub() would use name:
print "nom: [", ${$stuff_of{refaddr $self}}{$whatever_for_name}
+, "]\n";
}
# end added
sub dump_stuff
{
print Dumper($stuff_of{refaddr $_[0]});
}
1
}
my $s_obj = Spellaid->new();
$s_obj->set_name('the new name'); # set
print "task: [",$s_obj->get_task(), "]\n"; # get
$s_obj->a_complex_sub(); # spell check
$s_obj->dump_stuff(); # data dumper
__END__
for old_1 output:
# with misspelling
task: [the task]
Undefined subroutine &Spellaid::nmae called at <file> <line>
# without misspellin
task: [the task]
nom: [the new name]
$VAR1 = {
'name' => 'the new name',
'task' => 'the task'
};
output after commenting out old_1:
# with misspelling
task: [the task]
Use of uninitialized value in subroutine entry at <file> <line>
Can't use string ("") as a subroutine ref while "strict refs" in u
+se at <file> <line>
# without misspellin
task: [the task]
nom: [the new name]
$VAR1 = {
'_name' => sub { "DUMMY" },
'name' => 'the new name',
'_task' => sub { "DUMMY" },
'task' => 'the task'
};