Search code examples
perloopdelegatesmoose

How do you "lazy load" packages used as delegates?


Is there a way I can dynamically include a package based on whatever delegate is used rather than having to include all the various delegates?

I found this example on how to use delegates but it glosses over the details I'm trying to understand. The way this is written it's essentially all one file...

package Compare;
use Moose::Role;
requires 'compare';


package SpaceshipCompare;
use Moose;
with 'Compare';  

sub compare { my ($a, $b) = @_; return $a <=> $b }

package Sort;
use Moose;

has 'comparer' => (
    is       => 'ro',
    does     => 'Compare',
    handles  => 'Compare',
    required => 1,
);

sub my_sort {
    my ($self, @list) = @_;
    return sort { $self->compare($a, $b) } @list;
}

Usage:

my $sorter = Sort->new( comparer => SpaceshipCompare->new );
my @sorted = $sorter->my_sort("1one", "0", "43");

In my implementation of a delegate I'm using a different resource based on a parameter that's passed to the constructor.

  sub BUILD{
    my($this,$args) = @_;

        if($args->{cachedDataSource} eq 'local'){

            $this->setDataStore( Cache::LocalCache->new() ); 

        }

        if($args->{cachedDataSource} eq 'remote'){

            $this->setDataStore( Cache::RemoteCache->new() ); 

        }


        if($args->{cachedDataSource} eq 'memd'){

            $this->setDataStore( Cache::MemedCache->new() ); 

        }

}

But in order for this to work I have to

use Cache::LocalCache;
use Cache::RemoteCache;
use Cache::MemedCache;

Is there a better way to do delegates without perhaps having to use all the packages (like some kind of lazy load)?


Solution

  • In your example, you can simply use require:

    sub BUILD{
        my($this,$args) = @_;
    
            if($args->{cachedDataSource} eq 'local'){
                require Cache::LocalCache;
                $this->setDataStore( Cache::LocalCache->new() ); 
            }
    
            if($args->{cachedDataSource} eq 'remote'){
                require Cache::RemoteCache;
                $this->setDataStore( Cache::RemoteCache->new() ); 
            }
    
            if($args->{cachedDataSource} eq 'memd'){
                require Cache::MemedCache;
                $this->setDataStore( Cache::MemedCache->new() ); 
            }
    }
    

    Since require is a run-time operation, the class won't be loaded until it's actually needed. If your users were passing in class names, then it gets a bit more complicated. You might want to use Module::Load for that.