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

Hello my fellow Monks,

Please forgive me for I believe I have sinned. I believe I have broken the rule of "is-a" in order to serve my own selfish purposes. I use the CGI::Application framework for a lot of projects and one of the things I like to do is for each different section of the application I make a separate package as a separate file to keep things organized. For those who know how CGI::App is built and used there are plugin modules that take advantage of the Class (such as CGI::Application::Plugins::DBH.) When initialized it requires that you use $self as in my $dbh = $self->dbh();. So in order to use $self conveniently I abuse inheritance. For example:
package My::App; use strict; use warnings; use base qw(CGI::Application BLL::OtherPackage); sub setup { my $self = shift; $self->mode_param('section'); $self->start_mode('home'); $self->run_modes( 'here_sub' => 'here_sub', 'test_rm' => 'sub_in_other_file', ); } sub here_sub { my $self = shift; # no problems using self here } 1; ### Separate file package BLL::OtherPackage; use strict; use warnings; sub sub_in_other_file { my $self = shift; # $self would be what I expected if I didn't put this in # the 'use base' or @ISA for that matter. What an abuse. } 1;
As a long time programer of Perl I feel that this is something that I shouldn't be doing and that there is a correct (and better) way of getting a subroutine's $self to behave as if it were a method of a class even though it's in a separate file. I feel like I am doing something completely wrong. What is the best way to fix this and have methods to a class organised and separated in to separate files? I have Damian Conway's Object oriented Perl but it really hasn't given me any ideas as to how to deal with this.

My thanks for your help,
BMaximus

Replies are listed 'Best First'.
Re: Class Inheritance Abuse. Best way to fix it?
by stvn (Monsignor) on Jun 05, 2006 at 21:11 UTC

    I think philcrow has a good idea above. Basically you want to treat these files as if they are CGI::Application plugins. However, this solution is not without it's issues (exported methods conflicting, or overriding inherited methods, etc etc etc).

    As for the "is this good OO", the answer IMO is yes (kind of). What you are doing here is not all that different from what Perl 6 Roles try to do, which is to break down classes one step further. A role is not a class, but instead a collection of reusable methods which can be "injected" into a class's namespace.

    At $work, we have been experimenting with using Moose and more specifically Moose::Role for this type of problem. We have a base CGI::Application subclass which we all our applications inherit from, then we have a set of common roles which we can then add to our application. The roles include things like configuration file management, DBIx::Class schema setup and management, request logging and a number of other items which are useful in most (but not all) applications.

    And if you don't want to use Moose, you could also look into the other Role modules on CPAN. I would recommend Perl6::Roles or Class::Trait, with Class::Trait being the more "full-featured" of the two.

    -stvn
Re: Class Inheritance Abuse. Best way to fix it?
by philcrow (Priest) on Jun 05, 2006 at 20:26 UTC
    It might not make you feel better, but you could mixin, by having the other module explicitly export methods you need. This avoids some of the problems of multiple inheritence (it lets you pick and choose which methods you want and can make things more explicit, saving time with collisions happen).

    For example, drop BLL::OtherPackage from your use base list, but use it instead like this:

    package My::App; use strict; use warnings; use base qw(CGI::Application); use BLL::OtherPackage qw( sub_in_other_file );
    Then change BLL::OtherPackage so it exports:
    package BLL::OtherPackage; use strict; use warnings; use base 'Exporter'; our @EXPORT_OK = qw( sub_in_other_file ); sub sub_in_other_file { my $self = shift; # $self would be what I expected if I didn't put this in # the 'use base' or @ISA for that matter. What an abuse. } 1;
    In closing, I urge you not to guilty when you use Perl features to get your job done.

    Phil

      Hopefully he doesn't use BLL::OtherPackage elsewhere, and hopefully he'll remember to re-edit if he installs a new version.

      Not that I have a better idea right now. :-(

        It doesn't matter if BLL::OtherPackage is used elsewhere. The exporting code runs as many times as is required. If you say use BLL::OtherPackage qw( ... ) then an import call will occur. Period. BLL::OtherPackage is only compiled once but that's ok because you shouldn't need to keep recompiling it.

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊