in reply to Calling chains, extends, and subclassing

EvanCarroll

This is a common problem with parrallel class heirarchies, and one which no one has managed to solve well. It is sometimes referred to as the "Expression Problem", here is a paper which describes how it is solved in Scala using Traits. However, I don't think this particular solution would be possible using Moose.

The Moose-ish solution would be as follows:

package EvanCarroll::Quiz; use Moose; has 'question_class' => (is => 'ro', default => 'EvalCarroll::Question +'); sub new_question { my $self = shift; my $question = $self->question_class->new(@_); push @{ $self->questions }, $question; $question; }
Now, at this point you can do a number of things. The first one being using the constructor parameters for Quiz like so:
my $quiz = EvanCarroll::Quiz->new( question_class => 'My::Custom::Question' );
From what I understand of your design, this code would go into QuizMaster::Custom, which I assume would know what custom question class to use before it created the Quiz instance.

Of course you can also do the same in QuizMaster with the Quiz class, and get that to be dynamic as well.

Now if you want to allow the author of a custom Quiz subclass to supply an arbitrary Question class, and not enforce namespace consistency across Quiz and Question, then your user could just do this:

package Stvn::Quiz; use Moose; extends 'EvalCarroll::Quiz'; has '+question_class' => (default => 'Some::Random::Question');
The '+' in front of the attribute name tells Moose to clone the attribute from it's superclass (in this case EvanCarroll::Quiz) and to change just the 'default' value for question_class to be 'Some::Random::Question'.

And of course, this does not prevent your Quiz::Master class from still forcing the question_class parameter in the Quiz constructor (as shown above). Instead it only creates a new default for the new Quiz subclass.

Hope this helps.

-stvn

Replies are listed 'Best First'.
Re^2: Calling chains, extends, and subclassing
by EvanCarroll (Chaplain) on Oct 05, 2006 at 05:37 UTC
    Firstly, this is an awesome suggestion.


    How would suggest how to load these though, eval "require $self->question_class;"; before my $question = $self->question_class->new(@_); or is there a better way to do this too?


    Evan Carroll
    www.EvanCarroll.com
      ... is there a better way to do this too?

      Not really, UNIVERSAL::require is nice, but it pollutes UNIVERSAL, which is bad. Inside Moose we actually do this:

      my $file = $class . '.pm'; $file =~ s{::}{/}g; eval { CORE::require($file) }; confess("Could not load module '$class' because : $@") if $@;
      whenever we need to load a class. But aside from that I don't have a better suggestion.

      -stvn