Of late, I've been pondering the issue of private and protected methods in Perl. I actually like that Perl doesn't enforce them. In practice, the convention of preceeding "private" method names with underscores works quite well (as long as you include a note in the documentation explaining the convention). The pre-underscore convention fits the Perl spirit quite nicely: people who really want to mess with the class internally can do so, but they're on their own.

That said, though, there are times when slightly more robust enforcement of private and protected methods makes sense. Of course, there are relatively simply hacks to circumvent every approach I've seen, and that's OK: the idea is stronger discouragement, not complete protection.

There are no end of solutions to make method subs private, the most common one I've seen being some variation on:

sub _private_sub { die "_private_sub is private!" unless caller eq __PACKAGE__; # private stuff # }

The result is that only the package where _private_sub is defined can invoke it as a method. I've used this in the past where I found it sensible, and it has always worked well enough.

Unfortunately, such an approach does not address protected methods. Now, I've seen the term used a few ways, so let me define it: a protected method is a class method that may only be called by the package which defines it and by any class which extends the defining package. In other words, if I have ModuleA define _protected_sub_a, and ModuleB use base 'ModuleA', then both Modules can legally invoke _protected_sub_a directly. Were it a private sub, only ModuleA could invoke it.

With my current client, protected methods in Perl as defined above have become a requirement for me for the first time. What I came up with was a simple approach -- a base class that includes the following method:

sub PROTECTED { my $self = shift; my @caller = caller(1); $caller[3] =~ m{(.*)::(.*)$}; my ($pack, $sub) = ($1,$2); local $Carp::CarpLevel += 1; confess "method '$sub' is protected by '$pack'" unless ($caller[0]->can($sub)); return $self; }

If extending this base class (we'll call it BaseClass), we can do the following:

use base 'BaseClass'; # some stuff # sub _protected_sub { my $self = shift->PROTECTED; print "This is a protected sub!"; }

The end result is only this class and any which extend it can directly invoke _protected_sub. Of course, I know this isn't perfect protection and a consumer program could, with some work, cheat and call the protected methods. I'm OK with that, all I'm looking at is strong discouragement. In this case, attempt to inappropriately call a protected method (in this case, test.pl tried to call a method from a LocalTest2 object which extends the LocalTest class which in turn defines the test2 method) dies with something like:

method 'test2' is protected by 'LocalTest' at LocalTest.pm line 12 LocalTest::test2('LocalTest2=HASH(0x18633a4)') called at test. +pl line 15 main::test() called at test.pl line 20

While this is a Meditation, I'm also interested in seeking some wisdom. What are the potential problems of this approach? What existing solutions did I miss, and how are they better/worse?

Many, many monks here are wiser than I, and I'd appreciate any critique.

<radiant.matrix>
A collection of thoughts and links from the minds of geeks
The Code that can be seen is not the true Code
I haven't found a problem yet that can't be solved by a well-placed trebuchet

In reply to Private and Protected class methods by radiantmatrix

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.