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

Dear monks,

sometimes I'd like to treat the classes like objects created in compile time, with their constructors and initializers which can be inherited, modified by traits, etc... What I'm looking for is some support or methodology for such treatment.

Imagine hierarchy of classes (Class::DBI like) where each class keeps the array of columns. The columns are accessible via columns method, and modifyable via add_column. The array of columns is not modified after the class is loaded.

package MyPackage; use base qw(MyPackage::Base); __PACKAGE__->add_column('name'); __PACKAGE__->add_column('address'); 1;

Before the first call of add_column I wish to initialize the columns (with the list of cloned superclass's columns). After the last call of add_column I want to check all supplied columns and eventually add some default ones.

In other words I would probably like to silently perform something like:

my $class_body = sub { package MyPackage; use base qw(MyPackage::Base); sub initialize_class { my ($this) = @_; $this->add_column('name'); $this->add_column('address'); } 1; } __PACKAGE__->build_class($class_body);

where build_class would be inherited from MyPackage::Base.

I know that this particular case could (Class::DBI is the example) be easily solved by sophisticated columns method. Nevertheless I wonder if there is a methodology or design pattern for such class building. I appreciate any clue and apologize If I was not particularly clear about my intention.

Thanks for advice,

Roman

Replies are listed 'Best First'.
Re: Treating classes as objects
by merlyn (Sage) on Feb 13, 2007 at 13:59 UTC
    Take a look at the classic Class::Prototyped (which is the basis of my web framework, CGI::Prototype), or the more modern Moose, which will serve as the basis for the metamodel of Perl6. Both of them allow great reflection and introspection, and run-time behavior to dynamically add new member variables.
Re: Treating classes as objects
by kyle (Abbot) on Feb 13, 2007 at 12:27 UTC

    Doing something "before the first call to add_column" could be accomplished by adding your first time code to add_column, protected by a flag.

    my $_added_columns = 0; sub add_column { my $self = shift; if ( ! $_added_columns++ ) { $self->_initialize_class(); } }

    Update: Instead of testing some variable that's not used for anything else, you could test the data structure you use to store column information. That will work fine until someone does an add, remove, add sequence—if that's even possible.

    Detecting the "last call to add_column" is different. If you know that all your add_column calls are done before your first instantiation, you could do your initialization when that happens.

    my $_objects_created = 0; sub new { my $self = shift; if ( ! $_objects_created++ ) { $self->build_class(); } # construction continues }

    This is all kind of a clutter, though. There are stray private variables. There's an overhead to every new call as it checks to see if it's been called before. It would probably be a lot neater, if possible, to add it to where you add columns already.

    __PACKAGE__->initialize_class(); __PACKAGE__->add_column('name'); __PACKAGE__->add_column('address'); __PACKAGE__->build_class();
Re: Treating classes as objects
by jbert (Priest) on Feb 13, 2007 at 12:56 UTC
    You say that you want to do things "before the first call" and "after the last call".

    IMHO, the best way to achieve that is to make your API reflect it.

    So either require the caller to call a function before and after, or roll all those calls into a single function, "add_all_columns", which does the pre- and post- work internally.

Re: Treating classes as objects
by cdarke (Prior) on Feb 13, 2007 at 13:28 UTC
    One way would be to build an object of a "class of classes". The user would be required to call a constructor first, much like DBI->connect, which will initialise all that is required. This then returns a blessed ref which is used for a method call to create an object of the required class, much like getting a statement handle in DBI, by calling the prepare method on the database handle.
    Of course there is always the BEGIN block...