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


in reply to Private methods

Whether or not this code will cause problems depends upon how annoying the programmer finds the constructor's side effect of writing information to STDERR. Personally, I would not want that. However, I want to take a moment to go over your code because I see a few things that I might do differently. This isn't an answer to your question, per se, but more of a meditation regarding general coding style that might provoke interesting discussion.

01: my $_init_ = sub { 02: my $self = shift; 03: print STDERR ref($self) . "::_init_ called\n"; 04: }; 05: 06: sub new { 07: my $type = shift; 08: my $class = ref($type) || $type; 09: my $self = { 10: attribute1 => undef, 11: attribute2 => undef, 12: }; 13: bless $self,$class; 14: $self->$_init_(); 15: return $self; 16: }

I don't have a problem with you wanting to use a private method, but I disagree with how you're using it (I think the printing to STDERR was merely to show us how this works and not a programming technique you actually use, so I'll skip that). Instead, I'm more concerned with how you are using $_init_. Generally, I would see something like the above written as:

sub new { my $class = shift; my $self = bless {}, $class; $self->_init; } sub _init { # instance initialization }

If you have specific initialization for the instance of an object, it goes in _init and class initialization would be in new. (Many programmers put all of the initialization in _init() so that new() creates an object and _init() initializes it.) With that, subclasses can inherit new, get the appropriate class information and override _init to provide instance information appropriate for their class. However, this means overriding a private method!

This seems to be a highly contested issue with Perl OO programmers. Most feel that private methods should remain private and only the public interface should be exposed (which I now tend to agree with). Others seem to feel that you can override anything you want. If you need to get something done, sometimes the best way is to break the rules.

In the case of your code, you have an _init method that isn't initializing anything and can't be overridden. The latter may not be bad, the former however, is not good. It's not behaving the way I would expect it to and thus you haven't gained anything that I can see.

One way to deal with this might be to create two public methods. One is the constructor that sets class data. The other is initialize(), which sets instance data. If the class needs to be subclassed, the subclass only needs to override initialize(). This makes object construction a bit more cumbersome, though.

my $foo = Foo->new()->initialize(\%data);

That's not to say that private methods are useless, but I would want to see a more pertinent example before I could comment on how you are using it.

I'm also wondering about your lines 07 and 08:

07: my $type = shift; 08: my $class = ref($type) || $type;

I'm wondering why you have the ref($type) || $type in your code? That statement suggests that you might call new as a class method (Foo->new) and your might call it as an instance method ($foo->new). As a class method I understand, but as an instance method, it behaves as a class method, so why not call it as a class method? Calling it as an instance method can confuse programmers:

$employee{Sally} = Employee->new; $employee{Bob} = $employee{Sally}->new;

Without looking at the Employee package, I have no way of knowing what that's doing. Is Sally a really good worker so I decided that Bob is a clone of Sally? If I want to do something like that, I may as well just create a clone method to make the example more clear and to doom Bob to a life of gender confusion (not to mention the fact that the Catholic Church also objects to this practice):

$employee{Sally} = Employee->new; $employee{Bob} = $employee{Sally}->clone;

Unless you actually have code that uses the ref $proto || $proto construct, I would leave it out. But when might you need it? When a class method can also be appropriately called as an instance method. For example, I have a program where each object has a mapping of accessor/mutator names to the column names in the database. Thus, I can change the column name, update it in the object's map and I don't have to change the method names. But what happens if an object in a different class needs to get the column name? (for example, when needing dynamically built SQL.) I use the following method:

sub accessor_to_column { my ($proto,$column) = @_; my $class = ref $proto || $proto; # note the lexical variable to store class data my $found_column = 'id' eq $column ? $CLASS_DATA{$class}{id} : $CLASS_DATA{$class}{accessor_to_column}{$column}; unless ($found_column) { croak "No column found for ($column)"; } return $found_column; }

With this method, I might need to call it on a class, but I might also be calling it on an object instance. In both cases, the behavior must be the same, so the ref $proto || $proto trick is appropriate.

Thanks for letting me ramble and I hope I didn't take your code too seriously as an example of what you might actually be inclined to implement.

Cheers,
Ovid

New address of my CGI Course.
Silence is Evil (feel free to copy and distribute widely - note copyright text)

Replies are listed 'Best First'.
Re^2: Private methods
by adrianh (Chancellor) on Mar 09, 2003 at 23:17 UTC
    However, this means overriding a private method!

    Surely by definition, if you can override a method it isn't private :-)

    That's not to say that private methods are useless, but I would want to see a more pertinent example before I could comment on how you are using it.

    Where "private" methods win is when you want to hide your implementation decisions from subclasses. If you call your method as $self->_whatever you leave yourself open to having your code broken if a subclass adds its own _whatever method.

    You can also break existing code if you add a _something_else method to a later version of your base class when a subclass already has a method of the same name.

    Using lexically scoped coderefs or alternate calling strategies means we can code safe in the knowledge that a subclass is not going to change the intended behaviour of our "private" implementation methods.

      Surely by definition, if you can override a method it isn't private :-)

      Heh :) Perl is less modest about its private parts. When "private" methods are merely a matter of convention, scalability suffers. You bring this up quite nicely with you _something_else example. If Perl's OO was cleaner, fewer programmers would struggle with the subtle bugs of accidentally overriding private methods. Considered in that light, perhaps all private methods should be implemented via coderefs to ensure that this problem just goes away.

      The more I consider OO in Perl, the more I'm inclined to agree with tye's insistence that inheritance should be avoided (at least in Perl).

      Cheers,
      Ovid

      New address of my CGI Course.
      Silence is Evil (feel free to copy and distribute widely - note copyright text)

        The more I consider OO in Perl, the more I'm inclined to agree with tye's insistence that inheritance should be avoided (at least in Perl)

        Well - I wouldn't go quite that far :-) Although inheritance is often overused in designs - regardless of the implementation language.

        As annoying as these issues can be I find I don't get bitten by them very often. The problem is that it can be a complete bugger to track and fix when it does happen.

        There is also the problem that the lack of these abilities, even if they don't impact many real world situations, can be used in those pointless "perl cannot be used for coding in the large" flame fests :-)

        Perl is less modest about its private parts.
        That's a keeper. :)

        Makeshifts last the longest.

Re: Re: Private methods
by crouchingpenguin (Priest) on Mar 09, 2003 at 19:08 UTC
    I think the printing to STDERR was merely to show us how this works and not a programming technique you actually use, so I'll skip that

    correct.

    I'm more concerned with how you are using $_init_

    Pardon the naming... it was more or less just supposed to be an arbitrarily named private method.

    I'm wondering why you have the ref($type) || $type in your code

    Again, this was just my fingers typing away trying to make a cohesive example... usage for this was not the intended question and how it will be applied to the design is by no way intended to be examplified here.

    So forgive the lacking example. I was merely trying to flesh out the question so that it would not appear vague.


    cp
    ---
    "Never be afraid to try something new. Remember, amateurs built the ark. Professionals built the Titanic."
Re: Re: Private methods
by hardburn (Abbot) on Mar 10, 2003 at 15:25 UTC

    This seems to be a highly contested issue with Perl OO programmers. Most feel that private methods should remain private and only the public interface should be exposed (which I now tend to agree with). Others seem to feel that you can override anything you want. If you need to get something done, sometimes the best way is to break the rules.

    Java has class data which can be declared "protected", which means that classes in the same package can access it, but it's private data as far as anyone else is concerned. Protected methods can be overridden. I would say that the subclassible _init() would fall under the catagory of a protected method.

    As is always the case with Perl OO, there are no formal specifications for implementing protected data. Being that Java is the most OO-purist language in common use1, copying it's idioms for OO isn't such a bad thing. It's not like Perl hasn't copied things from everyone else.

    1 Yes, I realize that Java isn't a pure OO language, due to primitives like 'int'. The key phrase here is in common use. My personal theory about this is that truely pure OO langagues are so annoying that no one would want to build a real application with them. (And this is where a bunch of replies come in that tell me how wrong I am and pure OO langs are used all the time :)

    ----
    Reinvent a rounder wheel.

    Note: All code is untested, unless otherwise stated