Badger
view release on metacpan or search on metacpan
lib/Badger/Modules.pm view on Meta::CPAN
=head2 Example 4 - pre-loading modules (TODO)
# This doesn't work yet
use My::Project::Module
preload => 'foo bar baz';
=head2 Example 4 - pre-loading modules and exporting constants (MAYBE)
# Not sure about this.
use My::Project::Module
modules => 'foo bar baz';
my $object = FOO_MODULE->new;
=head1 DESCRIPTION
C<Badger::Modules> is a module for dynamically loading other modules that live
under a common namespace or namespaces.
It is ideally suited for loading plugin extension modules into an application
when you don't know in advance which modules may be required. An example of
such a module is L<Badger::Codecs> which loads L<Badger::Codec> modules for
encoding and decoding data in various formats.
It can also be useful even when you I<do> know what modules you want to use,
or think you do. Delegating the task of loading modules to a central
C<Badger::Modules> object allows you to easily change the modules that are
loaded at a later date, simply by changing the configuration for the
C<Badger::Modules> object.
C<Badger::Modules> can be used as a stand-alone module or as a base class for
creating specialised sub-classes of your own. A subclass of C<Badger::Modules>
can pre-define default values for configuration options. For example, you
might want to create a C<Your::App::Plugins> module that is configured to load
modules under the C<Your::App::Plugin> namespace by default.
Subclasses can also override methods to change the way it works or affect what
happens after a module is loaded. The L<Badger::Factory> module is an example
of such a module. It provides additional methods for dynamically creating
objects and relies on the underlying functionality provided by
C<Badger::Modules> to ensure that the relevant modules are loaded.
=head2 What's the Problem?
Consider the following code fragment showing a subroutine that creates and
uses a C<Your::App::Widget> object.
use Your::App::Widget;
sub some_code {
my $widget = Your::App::Widget->new;
$widget->do_something;
}
One of the benefits of object oriented programming is that objects of
equivalent types are interchangeable. That means that we should be able to
replace the C<Your::App::Widget> object with a different
implementation as long as it has the same interface in terms of the methods it
implements. In strictly typed programming languages this equivalence is
enforced rigidly, by requiring that both objects share a common base class,
expose the same interface, implement a particular role, or some other
mechanism. In loosely typed languages like Perl, we have to rely on duck
typing: if it looks like a duck, floats like a duck and quacks like a duck
then it is a duck (or is close enough to being a duck for practical purposes).
For example, we might want to use a dummy widget object for test purposes.
use Your::App::MockObject::Widget;
sub some_code {
my $widget = Your::App::MockObject::Widget->new;
$widget->do_something;
}
Or perhaps use a C implementation of a module on platforms that support it.
use Your::App::XS::Widget;
sub some_code {
my $widget = Your::App::XS::Widget->new;
$widget->do_something;
}
Or maybe an implementation with additional debugging facilities for use
during development, but not in production code.
use Your::App::Developer::Widget;
sub some_code {
my $widget = Your::App::Developer::Widget->new;
$widget->do_something;
}
By now the problem should be apparent. To use a different implementation of
the widget object we have to go and manually change the code. Every occurrence
of C<Your::App::Widget> in every module of your application must be
changed to the new module name. Of course, if you were doing this in real
life you would probably end up defining a variable to store the name of the
relevant class. Something like this perhaps.
use Your::App::Widget;
our $WIDGET_CLASS = 'Your::App::Widget';
sub some_code {
my $widget = $WIDGET_CLASS->new;
$widget->do_something;
}
This works well in simple cases. However, if you've designed your application
to be suitably modular (thereby promoting reusability of the individual
components and extensibility of the system as a whole) then you may have a
whole bunch of different modules to load, all of which need similar variables.
use Your::App::Widget;
use Your::App::Doodah;
use Your::App::Thingy;
our $WIDGET_CLASS = 'Your::App::Widget';
our $DOODAH_CLASS = 'Your::App::Doodah';
our $THINGY_CLASS = 'Your::App::Thingy';
( run in 1.974 second using v1.01-cache-2.11-cpan-97f6503c9c8 )