http://qs321.pair.com?node_id=484432

gargle has asked for the wisdom of the Perl Monks concerning the following question:

I often use one of the following constructs for my private methods. Both have their benefits (the first gives a compile error, the second one dies during runtime but is easier to read). However, I wonder if there are other, better ways?
# private method add my $add = sub { my $self = shift; my $amount = shift; $self->balance($self->balance+$amount); };
or
# private method padd sub padd { my $self = shift; my $amount = shift; caller(0) eq __PACKAGE__ || die "private method"; print caller(0) . " " . __PACKAGE__; $self->balance($self->balance+$amount); }

Replies are listed 'Best First'.
Re: OO - best way to have protected methods (packages)
by tye (Sage) on Aug 17, 2005 at 14:43 UTC
    package My::Class::_Implementation; sub _add { my $me= shift @_; # ... } sub _init { my $me= shift @_; # ... } *My::Class::instancePackage= sub { my $class= shift @_; return $class . "::Object"; }; *My::Class::new= sub { my $us= shift @_; my $me= bless {}, $us->instancePackage(); _init( $me ); return $me; }; *My::Class::Object::new= sub { my $me= shift @_; my $new= bless {}, ref($me); _init( $me ); return $me; }; *My::Class::Object::method= sub { my $me= shift @_; _add( $me, @_ ); };

    If you don't like the "*My::Class::..." part, then you can instead export your methods to My::Class and/or My::Class::Object depending on whether they are class methods or object methods. Lots of ways to do that.

    This technique prevents using a class method on an object, prevents using an object method on the class, makes it easy for you to use your private subroutines, makes it very clear that they are private, but doesn't prevent someone from getting their job done when they realize that your class design doesn't allow for them to do their job portably so they need to implement a hack that does something with your private subs that will likely break in some future version of your module (while not having to modify your module which might be being used portably elsewhere).

    - tye        

Re: OO - best way to have protected methods
by izut (Chaplain) on Aug 17, 2005 at 14:21 UTC
    I think the second example in your post is the usual behavior in any OO language. I would advice you to use croak() or confess() - from Carp - in place of die().


    Igor S. Lopes - izut
    surrender to perl. your code, your rules.
      Obrigado, thanks you,

      I'll put your advice to good use. It's in line with what I do for protected methods:
      # protected method printMe, silly example actually sub printMe { my $self = shift; caller(0)->isa(__PACKAGE__) || confess "cannot call protected +method\n"; # do something silly for now print $self->account . " " . $self->name . " " . $self->balanc +e . "\n"; }
Re: OO - best way to have protected methods
by adrianh (Chancellor) on Aug 17, 2005 at 14:39 UTC

    See Private method variations for some more ideas.

    Of the two you offered I would prefer the lexically scoped coderef because you can still use the $o->$add( 42 ) calling style and not worry about subclasses accidentally breaking your code.

Re: OO - best way to have protected methods
by Joost (Canon) on Aug 17, 2005 at 14:40 UTC
Re: OO - best way to have protected methods
by BaldPenguin (Friar) on Aug 17, 2005 at 14:58 UTC
    One of the links inside the page adrianh sent us to was about InsideOut Objects mainly involving use of some cpan classes to get the privacy, very effective. Damian also discusses this in his Perl Best Practices book, it's good reading.

    Don
    WHITEPAGES.COM | INC
    Everything I've learned in life can be summed up in a small perl script!
      Insideout objects are angerous with threads though, due to windows/unix differences -- unless you're real real careful. And Damian may not have warned his readers enough in the book: Threads and fork and CLONE, oh my!.
Re: OO - best way to have protected methods
by Transient (Hermit) on Aug 17, 2005 at 14:45 UTC
    perltoot recommends the usage of the Alias module, although I have to say I've never tried it myself.
Re: OO - best way to have protected methods
by astroboy (Chaplain) on Aug 17, 2005 at 20:19 UTC
      How do these two compare to my solution performance wise? The syntax is nice of course but it looks to me as if there are more method calls involved behind the scenes? Am I right?

      (It's been 3 years ago since I last did anything serious, >50 lines, in perl... So I am a bit rusty. Java does that to you, you know ;)
        Well, you'd best benchmark it. If you're calling methods in a tight loop it might be a problem, but if you look at the code - say of Attribute::Protected - you're simply calling one other explict method and one anonymous sub for an attribute that you assign to your methods. That slight penalty is offset by the readability conferred IMHO
Re: OO - best way to have protected methods
by radiantmatrix (Parson) on Aug 17, 2005 at 18:21 UTC

    Just sort of "thinking out loud", here...

    With the above in place, you could create your private add sub like:

    use Private; sub _add { is_private; ## die if called outside of this package my $self = shift; my $amount = shift; $self->balance($self->balance+$amount); }
    <-radiant.matrix->
    Larry Wall is Yoda: there is no try{} (ok, except in Perl6; way to ruin a joke, Larry! ;P)
    The Code that can be seen is not the true Code
    "In any sufficiently large group of people, most are idiots" - Kaa's Law
      As currently implemented, that only works in the first module that calls is_private. Witness:
      package Foo; use Private; sub foo { is_private; } package Bar; use Private; sub bar { is_private; } package main; Bar->bar;
      main cannot call Bar::bar (private to Foo)

        Yeah, that module took me all of five minutes. It works if you use or require several different modules, but not if you create several in the same file. I don't know why this is, and therefore I don't know how to fix it. Patches and/or explanations would be welcome.

        And, BTW, it's not the first package to call is_private, but rather the first to use Private -- the init is done (and the 'private' package name determined) during import.

        I don't really have the drive to create something to solve this problem, as I don't really consider it a problem: I use the suggested syntax for methods/subs/variables I wish to be private (prepend the name with an underscore), and document that these are not intended for consumption outside the module code.

        If people want to shoot themselves in the foot by using subs/etc. I've marked as "I want this to be private", then I have no problem letting them. ;-)

        <-radiant.matrix->
        Larry Wall is Yoda: there is no try{} (ok, except in Perl6; way to ruin a joke, Larry! ;P)
        The Code that can be seen is not the true Code
        "In any sufficiently large group of people, most are idiots" - Kaa's Law
Re: OO - best way to have protected methods
by gargle (Chaplain) on Aug 17, 2005 at 19:36 UTC
    Sorry about the confusion about protected and private. I've came up (thanks to the replies) with the following 'system' to use.


    package Base; # Base class to experiment with private and protected methods use warnings; use strict; use Carp; sub new { my $type = shift; my $class = ref $type || $type; my $self = { TEXT => undef, SECRET => undef, NOTSOSECRET => undef, }; my $closure = sub { my $field = shift; if (@_) { $self->{$field} = shift; } return $self->{$field}; }; bless ($closure,$class); return $closure; } # a public accessor to set TEXT sub text { &{ $_[0] }("TEXT", @_[1 .. $#_]) } # a private accessor to set SECRET sub private { caller(0) eq __PACKAGE__ || confess "private method"; &{ $_[0] }("SECRET", @_[1 .. $#_]); } # a public accessor to set SECRET by means of the private method sub secret { private($_[0],@_[1 .. $#_]); } # a protected accessor to set NOTSOSECRET sub protected { caller(0)->isa(__PACKAGE__) || confess "protected method\n"; &{ $_[0] }("NOTSOSECRET", @_[1 .. $#_]); } 1;


    package Inherit; # Inherit class to experiment with private and protected methods use warnings; use strict; use Base; use vars qw(@ISA); @ISA = qw(Base); sub inheritSecret { $_[0]->private(@_[1 .. $#_]); } sub inheritNotSoSecret { $_[0]->protected(@_[1 .. $#_]); } 1;


    The main routine that calls all:
    #!/usr/bin/perl use warnings; use strict; use Base; use Inherit; my $base = Base->new(); $base->text("hello"); print $base->text . "\n"; my $inherit = Inherit->new(); $inherit->text("howdy"); print $inherit->text . "\n"; $base->secret("secret"); print $base->secret . "\n"; # this will get us an error saying private method #$inherit->inheritSecret("verySecret"); #print $inherit->inheritSecret . "\n"; # impossible to access protected directly #$inherit->protected("notsosecret"); #print $inherit->protected . "\n"; # access to NOTSOSECRET by means of a protected method $inherit->inheritNotSoSecret("notsosecret"); print $inherit->inheritNotSoSecret . "\n";
      It just occured to me that I now have public, private and protected variables thrown in for free... TEXT, SECRET and NOTSOSECRET are fully accessible by package Base. Other packages (has-a's and is a's) have to use methode calls.

      Classes which inherit from Base can work with TEXT and NOTSOSECRET.Classes which don't inherit can only access TEXT.

      This works for me I guess. The overhead of calling a method to set/get a variable I can live with, and, actually put to good use to check the setters for invalid parameters.

      Thanks, all, for the comments and the links!