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


in reply to Re: Re^4: USE or Require?
in thread USE or Require?

You just gave off a few whiffs of bad practice and I'd like to clarify just what you mean. There are some wrong answers here and I'm hoping you'll either confirm or deny that you're being sane.

Exporting new() so it can be used as a function and not just as a method.

use l2kashe 'new'; my $o = new; $o->foo( ... ); # Vs use l2kashe; my $o = l2kashe->new; $o->foo( ... );

This is bad practice for a number of reasons. I'll cover whatever comes to mind just now. Keep in mind that I'm internally recoiling from the really hideous things you've brought to mind here.

'new' is the common name for a constructor method. If the importing code already had a 'new' method or function then you've just overwritten the module's own new() method. 'use warnings "redefine"' will catch this and throw a warning but it won't produce a fatal error. In fact, because use() happens at compile time, which version of new() remains in your user's code is up to the order in which they were mentioned.

use l2kashe; # The importing new() dies sub new { ... } # This one stays # Vs sub new { ... } # The original new() dies use l2kashe; # This one overwrites

The expectation is that the constructor is tied to the class it originates in. Your constructor will have to hard code the originating class so that it can still reference l2kashe instead of wherever it was copied to. We all know hard coding data like this is poor form.

sub new { bless {}, shift } # Vs sub new { bless {}, __PACKAGE__} # or maybe even: sub new { bless {}, 'l2kashe' }

Your constructor has to function as a method and a function now. This is highly irregular and is one of the funkiest things that CGI.pm does. You have to include code in new() that decides whether $_[0] is the class name it should use (if it was called as a method) or whether it is just the first parameter (if it was called as a function). Properly done, the first argument is always the class name. That detail is handled by calling the constructor as a method - this is why you often see code that looks like this

sub new { bless { @_ }, shift }

In your case you have to distinguish between strings that are class names and strings that are parameters. Its possible but you've now eliminated the class name as an available value to the first parameter.

sub new { my $class_or_parameter = shift; if ( $class ne __PACKAGE__ ) { unshift @_, $class_or_package; } bless { @_ }, __PACKAGE__; }

You also intimated that maybe your other functions/methods also are expected to be called as both functions or methods. In this case you have to be even tricker and introduce significant evil to all of your code.

sub something { my $self_or_parameter = shift; if ( ref $self_or_parameter eq __PACKAGE__ ) { unshift @_, $self_or_parameter; } ... }

For the moment anyway, I can't see anything but really evil things down this path. Object oriented perl code just does not use Exporter. There is 100% no need for it. the expectation is that your constructor is a method call using the class on the left side of the arrow: SomeClass->new. Some people argue you should be able to do $o->new, I'll not address that but certainly out is SomeClass::new. Additionally, all of your methods that are not constructors are always called as methods and always with the left side being the object in question. So $o->method, never SomeClass->method or SomeClass::method.

Replies are listed 'Best First'.
Re: Re: Re: Re^4: USE or Require?
by l2kashe (Deacon) on Jul 24, 2003 at 22:17 UTC

    Ok, that makes sense, but that isn't what Im talking about. My constructors are always

    sub new { my $class = shift; my $obj = {}; # possible other initialization here # testing for args, making sure defaults are sane etc.. bless($obj, $class); # possible havy duty init stuff here # init new objects inside this one, make expensive # connections now, if its Ok to do that. $obj; } # END sub new

    In regards to the function/method conundrum.I only write methods. first line is always
    my $self = shift; or my($self,@other_stuff) = @_;
    I tend to have each method self sustaining and not necessarily reliant on other methods, I.e if inserting data, make sure its going to be clean when I pull it out, if some resource should exist (an open socket for instance) and it doesn't, either bomb out or possibly initalize it now. This way, if I have solved a problem once, then I can pick up a few methods from module_a, and drop into module_b with relative ease. I can see where you are going with the thread though, and I simply didn't realize the horrific road that could come from a simple @EXPORT_OK = qw(new); To me it means, the only thing you can get at is new, and I expected the coder to write $obj = l2kashe->new(@args); (yes I understand that expectations are not always what happens) I try to always provide at least rudimentary documentation, and always note the appropriate way to use the module is

    use L2kashe; my $l2 = L2kashe->new();

    Thanks for taking the time to clarify, and my apologies to the other monks for not understanding the depth of the issue. I'll just drop use Exporter and those variables from my code until truly needed. Again, thanks

    On a side note, it might do to put a note in the tutorial about stub module generation via h2xs -A -X -n some::module, as that is where I saw use Exporter, assumed it was good practice to use (yes I realize how bad that is now in hindsight), and defined what I thought was a sane value.

    A humbler monk

    use perl;

      On a side note, it might do to put a note in the tutorial about stub module generation via h2xs -A -X -n some::module, as that is where I saw use Exporter, assumed it was good practice to use (yes I realize how bad that is now in hindsight), and defined what I thought was a sane value.

      On a side note to your side note, if you're using perl 5.8.1 h2xs has a few more options that make a more "normal" OO template. My default is:

      h2xs --compat-version=5.6.1 --use-new-tests --skip-exporter -APX -n