[CDBI] Different subclasses for one table / Factory Pattern?

Stephan Brunner stephan.brunner at gmx.de
Thu Sep 14 20:17:22 BST 2006


I wrote:
> > You have probably come up with a solution by now, I'm sure.  Let us
> > know what you ultimately came up with.
>
> Hmm, well, yes, somehow... But I didn't have the time to implement my
> solution, and, in my experience, the solution might change during
> implementation;-)
>
> For the next weeks, I'm away from the computer, so please be patient. But I
> will let you know and I'm also interested in the discussion!

Here I am back again.
Looking at Oliver's episodes, I'm quite happy about the solution I came up 
with: I kept everything related to different-subclasses-per-table away from 
CDBI.

I introduced a class FileType with several subclasses (FileType::JPG, 
FileType::AVI etc.). To the class representing the database table ("files"), 
I added an object property "type", wich keeps an object of the relevant 
subclass of FileType. Looks like so:

package File;
use base 'Class::DBI';
# ...
sub type {
    my $self = shift;
    if (not exists $self->{type}) {
        $self->{type} = FileType->new($self);
    }
    return $self->{type};
}

package FileType;
sub new { 
    shift; # throw class away
    my $file = shift;
    my $name = $file->name();

    my $class = undef;
    my @classes = qw/JPG WAV AVI/;
TRY: foreach my $try_class (@classes) {
        $try_class = __PACKAGE__ . '::' . $try_class;
        # passing $name explicitly avoids the call of $file->name in
        # every is_member:
        if ($try_class->is_member($name, $file)) {
            $class = $try_class;
            # first try_class saying "yes, $file is_member!" gets selected
            last TRY;
        }
    }
    if (not defined $class) {
        $class = __PACKAGE__ . '::Unknown';
    }

    # create and return an object of the selected class;
    # keep a reference to the $file object for later use
    my $self = bless { file => $file }, $class;
    return $self;
}
# ...

package FileType::JPG;
# for example
sub is_member {
    shift;
    my ($name, $object) = @_;
    return ($name =~ /.jpe?g$/i) ? 1 : 0;
}


One big drawback is the additional method calls I get. To rotate a jpg, for 
example, I call $file->type->rotate(), as rotate is a method of 
FileType::JPG.
And, even worse, in FileType::JPG::rotate (called on the FileType object 
stored in $file->type), I call $self->file() to get the file that actually 
will be rotated. To avoid this, I would have to call 
$file->type->rotate($file), which also looks strange to me.

Well, this obviously is no perfect OO design. On the other hand, it is quite 
clear and obvious in how it works as I'm not using nor overriding any CDBI 
internals.
Anyway, I'm not perfectly happy about it, so any comments still very welcome!

Regards,
Stephan



More information about the ClassDBI mailing list