Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Packages with subs using fully qualified class names, not always their own.

by SimonSaysCake (Beadle)
on Jun 30, 2017 at 19:54 UTC ( [id://1193951]=perlquestion: print w/replies, xml ) Need Help??

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

I'm working on a helper package and was looking at the code of another that has a subroutine I want to use and noticed that some of this other package's subroutines included a fully qualified package name. Then I noticed that it had no init ("new") sub and that these qualified names were the "parent" package and that, via this method, the author was extending the functionality of the other "parent" package but without specifically sub-classing it. The package in question is Net::FTP::File and all of its subs use this format:

sub Net::FTP::new_ftp_action { do stuff; }

So when you include this Net::FTP::File package you can just do:

use Net::FTP::File; my $ftp = Net::FTP->new($ftp_host) or croak; $ftp->login($user, $pass) or croak; $ftp->new_ftp_action($args);

Which is pretty slick in a way; reminds me a little of how Javascript packages can be extended.

I wondered how many other packages I had in my Strawberry install folder that behaved the same or similarly so I wrote a little File::Find-er util. I didn't find anything that worked quite like the Net::FTP::File package but did find others that had similarly named subroutines. One example is the Data::Dumper::HTML package which includes this:

sub Data::Dumper::DumpHTML {

Also of note is the Crypt::OpenSSL::X509 package which has a sub that uses its own fully qualified package name:

sub Crypt::OpenSSL::X509::has_extension_oid {

instead of just using:

sub has_extension_oid {

for reasons I don't fully comprehend.

My question is, what does the collective think of extending existing packages in this way? Is it better to just sub-class the parent (assuming that works) even if your "new" subroutine will essentially be a pass-through to SUPER::new? On that note, what about creating wrapper "child" classes that handle parent class method calls (not specifically overloaded) via an AUTOLOAD mechanism (I could see this being useful when sub-classing the parent doesn't work for some reason)? Best practice in this case?

Replies are listed 'Best First'.
Re: Packages with subs using fully qualified class names, not always their own. (monkey patch)
by LanX (Saint) on Jul 01, 2017 at 01:33 UTC
    What you describe is called a "Monkey Patch"

    The problem (if not properly done) are uncontrollable distant effects, because these changes are global, and another module in the same runtime using the same "parent" class might be sabotaged by the changes.

    Contrary to this subclassing will only effect the subclass.

    I'm using monkey patching in one of my modules, but took care to localize it.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      "I'm using monkey patching in one of my modules, but took care to localize it."

      It might be of interest how you did it. Perhaps you like to show it?

      Best regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

      perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

        I only changed the "alien" routine temporarily

        Simplified something like

        sub my_func { local *OtherPackage::routine = sub { ... } # now use OtherPackage }

        local will assure that the old code is restored when the scope is left and you have to keep full control over the code inside the scope.

        This is not 100% safe because an exception could occur in the middle and a SIG handler could theoretically use OtherPackage.

        in my case this is pretty impossible, but to be safe you could additionally check caller inside the patched routine.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re: Packages with subs using fully qualified class names, not always their own.
by roboticus (Chancellor) on Jul 01, 2017 at 00:16 UTC

    SimonSaysCake:

    It's one of the advantages of perl, IMO. One of the problems with object-oriented programming, as I see it, is that there's an internal pressure to make your classes "complete" so that users can do all the tasks they would reasonably expect to be able to do with the class. Many languages require that a class be created in a single source file, so frequently you'll get occasionally useful functionality thrown into the file, when most applications don't actually need it. As the class ages, more and more stuff gets put in there.

    The ability to add some functionality to a class without inheriting from it is pretty nice, as you can simply include other modules at the top of your program. So if you need some rarely-needed functionality, you don't need to subclass something to add the functionality and then update your script to use the new subclass. Using your extra functions with another package of extra functions is similarly easy, just add the functions into your class and you're done. No need to refactor your application, no need to worry about how to refactor the extra functions into yet another class hierarchy to let them coexist.

    If it's truly a one-off function, you can just add it into your script, and it'll still act as if it were part of the package proper.

    It's not something you'll need to do all the time, but it's flexible and clean, compared to having to worry about inheritance headaches.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      "...The ability to add some functionality to a class without inheriting from it is pretty nice..."

      Yes. But isn't this the thing about "what a class does"?

      Perhaps it might be better in such a case to use a plugin: Let it role, baby, role.

      Just some callow thoughts. Please correct me if i'm wrong.

      Best regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

      perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

        karlgoethebier:

        You're not wrong at all. Roles could be a good way to do it.

        Generally I do it when:

        • It's purely a one-off thing that I'm trying to solve, and don't expect to ever need it for the class again. A frequent case is when I'm debugging something complicated that relies on a little bit of inner state of the class and a custom "toString" function in the class for logging could give me the data I need to solve the problem.
        • The class API isn't fully resolved and some of the implementation is a bit hacky. I don't want to expose the hackiness to the classes API but I need to get a problem solved. So I have no problem accessing the internals in an ugly fashion to make something happen, and let the class evolve normally without putting an ugly hack in the API that may be troublesome to remove later. (Such as when John Smith sees it and immediately uses it in production....)

        I'm not saying it's a good/best practice, but that it sure is convenient.

        Sorry for the long delay in replying. I've ignored my Chatterbox queue a bit too long, and now I'm trying to work through the backlog...

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1193951]
Approved by Paladin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (2)
As of 2024-04-25 06:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found