perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I've been reading through Conway's on object oriented programming and don't see any ready solutions. There are plenty of ways to enforce privacy of data, but he doesn't describe ways to *ensure* (for my own benefit) that other classes don't use private methods of other classes.

The only thing I've thought of is to keep all the private routines defined in a "setup" routine that stores all the pointers to the methods and future calls only go through indirect calls.

My solution to data privacy wasn't one suggested by Conway (perhaps with good reason?) but I decided to create a package "frame" - one/package. I can likely extend that to encompass my private methods.

My current package namespace looks something like this: (majorly trimmed output from DataDumper of the top-most class's ref returned to "main" )

$s = bless( { 'Entry_Display_in_Wins' => { _GUI_Glue' => bless( { 'GUI_Glue' => { '_image_win'=>bless( {},'Tk::Photo'), '_main_win' =>bless( {}, 'MainWindow'), } }, 'GUI_Glue' ) }, 'Entry_Ordering' => { '_auto_advance_time' => 6, '_chances' => [], '_chances_total' => 0, '_cur_entry_index' => -2, '_disable_auto_advance' => 0, '_formula_text' => '$x*$x', '_rating_to_chance' => sub () { package Entry_Ordering; use strict 'refs'; my $x = shift @_; $x = $x*$x;; }, '_record_index' => -1, '_shuffle' => 0, 'record' => [] }, 'File_Rating_DB' => { '_base_dirname' => '/home/$USER/img-redirect/', '_db_filename' => 'show.dat', '_db_modified' => 1, }, 'File_Rating_Entries' => { '_entries' => [ bless( { '_filename' => 'Andromeda-m31.jpg', '_rating' = +> 2 }, 'File_Entry' ), bless( { '_filename' => 'Hibiscus_1.jpg', '_rating' => 9 + }, 'File_Entry' ) ] } }, 'Timed_Display_In_Wins' );
So I was thinking -- I could segregate private methods by storing coderefs in "variables" and call indirectly through the variable names. I can do an access check to catch calls to "private methods" called from a different class.

B::Lint catches calls to private methods, but isn't too helpful since it flags *every* call to a "private method" (ones beginning with "_") and claims it may be broken on "most" (any thread-enabled) Perls.

Anyway, just hoping to get some feedback on how others enforce object "privacy". I haven't run into anything on perl-object privacy where objects include methods as well as data. Have seen a few on data_privacy, closures and the like, but the data is only "half" the object or class (and usually the bulk of the work! :-)).

I don't need method privacy at any greater level than that which I have above for class fields. I.e. the data is accessible if they explicitly go through class fields but no accidental accesses like I've had with methods. It can be especially problematic if you split an existing class (or set of linear actions into one or more hierarchical classes like package "Array_In_File" being split into "Array_Ops" and "File_Ops", and want to make sure no there's no "unauthorized" semi-private method hanky-panky. Linda W.

Replies are listed 'Best First'.
Re: Howto keep private methods private?
by erroneousBollock (Curate) on Sep 14, 2007 at 05:34 UTC
    Are you sure you've read the section on encapsulation? It describes many ways.

    I most like the suggestion to bless a sub-ref (the accessor) which closes over the object payload.

    In that scheme instance methods must call the accessor to gain access to the now-invisible data; you can also use caller() in your accessor to ensure that whatever semantics for ecapsulation you desire is enforced.

    -David

Re: Howto keep private methods private?
by citromatik (Curate) on Sep 14, 2007 at 09:29 UTC
      so, basically that's similar to the variable security mechanisms using random key names.

      lots of different ways...

      I somehow thought the lack of explicit examples of hiding private method names might not been done because it was messier than variable hiding. It is. Not by much, but a smidge. :-)

      Thanks!

Re: Howto keep private methods private?
by Joost (Canon) on Sep 14, 2007 at 14:41 UTC
    package Whatever; { my $priv1 = sub { my $self = shift; # do something private }; sub public { my $self = shift; $self->$priv1(); } }
    No need to check access since you cannot access $priv1 from another scope.

    update:

    This is the probably the strongest protection you can get. Barring things like PadWalker (usually not relevant) or explicit passing around of the subroutine reference, there really is no way at all to get at the sub outside of the scope.

    I have never used this in anything serious since I don't see the point of enforced private (or protected) methods, and it's a little bit more work.

    Also, this construct is not exactly equivalent to a private method as used in, say, C++, since it's based on lexical scoping, instead of class/package scoping.

    Note that it's trivial to insert/redefine methods into a perl package, so "naive" "private" methods are no guarantee in perl, anyway.

Re: Howto keep private methods private?
by kyle (Abbot) on Sep 14, 2007 at 12:21 UTC

    Class::Std creates something like this:

    sub private { my $caller = caller; croak "Can't call private method from $caller" if $caller ne __PACKAGE__; }

    That's paraphrased. If you want, you can check caller against an access list or do other funny things.

      That approach to method privacy is fundementally broken, consider this:

      package Foo; sub new { bless {} => shift } sub baz { "Hello from Foo::baz" } package Bar; use base 'Foo'; sub baz { # baz is private ... my $caller = caller; croak "Can't call private method from $caller" if $caller ne __PACKAGE__; "Hello from Bar::baz (private)"; } sub call_baz { (shift)->baz } package main; Bar->new->baz; # BOOM!
      This is incorrect behavior, private methods should not block a perfectly valid (inherited) public method from being called. The only way to get proper behavior is to not add the private method to the class's symbol table, which means doing it like so:
      package Foo; sub new { bless {} => shift } sub baz { "Hello from Foo::baz" } package Bar; use base 'Foo'; my $baz = sub { # baz is private ... my $caller = caller; croak "Can't call private method from $caller" if $caller ne __PACKAGE__; "Hello from Bar::baz (private)"; }; sub call_baz { (shift)->$baz() } package main; Bar->new->baz; # no more BOOM!

      -stvn

        private methods should not block a perfectly valid (inherited) public method from being called.

        Why not? You're advocating a workaround to allow someone to name a private method the same as a public one. That's asking for trouble.

        private methods should not block a perfectly valid (inherited) public method from being called. The only way to get proper behavior is to not add the private method to the class's symbol table

        I agree, kind of. I agree with the problem description and it's a fundamental issue with Perl's OO mechanism. I don't agree with your solution. (It's not completely clear you actually suggest people do use lexicals though.)

        However, if you only want to make sure you don't block a parent method you can proxy the method call using goto &subname; (and possibly override can if there is any uncertainty as to whether the parent method exists or not), like

        sub baz { # baz is private ... goto &{$_[0]->can('SUPER::baz') || \&error} if caller ne __PACKAGE__; return "Hello from Bar::baz (private)"; }
        But this solution only solves half the problem with having private methods. It doesn't solve the problem of a child accidentally hijacking the call. So for me it's not good enough to make it worth the trouble.

        My real issue with the lexical approach is this. If you extend your concept, then you can't write or import utility functions either, as they might block a parent's methods. You'd only be allowed to put public methods in the symbol table. But that's just too much a mess. So unless you use an OO framework that solves this for you, you just have to make sure you don't have private methods or (imported) functions that clash with any parents' method. It adds some danger, but subclassing a class you don't control yourself is already dangerous for several other reasons.

        This is my rationale behind the paradigm described here, which takes the middle road:

        1. call private methods as function (no risk of hijacks)
        2. don't name public methods with a leading underscore (always safe to add _func:s, if you follow (1))

        lodin

        I think if I inherit and override a public method with one that is private (or otherwise breaks the public interface), that's certainly rude, but I would not say that's a problem with the way the privacy is implemented. Perhaps we have different definitions of privacy at work here. I was trying to make a method that outsiders cannot call. You appear to want a method that outsiders cannot see at all.

Re: Howto keep private methods private? ('function' lookup)
by lodin (Hermit) on Sep 14, 2007 at 13:09 UTC

    ... ways to *ensure* (for my own benefit) that other classes don't use private methods of other classes

    Do you mean that you want to ensure that your class (being a parent class) doesn't accidentally call a private method in a subclass? If you're talking about method calls that look like

    $self->_private(@args)
    then maybe I have something for you.

    I don't ever call private methods using method lookup, since I know which package it will be in, namely the current package. I also name private methods with a leading underscore, and thus my code looks like this:

    _private($self => @args)
    This way, I don't ever have to be afraid to refactor a parent class and add a private method. If you use method lookup, you'll risk accidentally picking up a private method in the subclass, thus needlessly breaking distant code.

    This is a paradigm that has worked well for me. It's simple, it doesn't require anything out of the ordinary, and works well enough (but YMMV).

    If you talk about what Java calls protected methods (methods that are designed to be used by subclasses too, but no one else), then that's a whole different story.

    lodin

Re: Howto keep private methods private?
by CountZero (Bishop) on Sep 14, 2007 at 19:09 UTC
    Documentation is the key.

    Explain your private methods in the documentation and make it very clear they are private and only to be used through the provided public interface. If someone chooses to disregard that friendly advice and wipes out his company's datastore -- too bad: he has been warned.

    It is not as if you use private methods, leading underscore and all, by mistake.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      I despise seeing private methods in public documentation. That belongs in a document like Foo::Internals or somesuch. Something that makes it obvious this is for developers only, not consumers.

      The point of documenting your contract is twofold:

      1. You're telling people what they can do (informational)
      2. You're telling people what you promise (legal)
      If someone does something outside of what you promised and it hurts them, then that's their problem. You aren't responsible for my asinine abuse of your product.

      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?