Why not adopt me?
NAME
Class::ClassDecorator - Dynamically decorate classes instead of objects using NEXT
SYNOPSIS
use Class::ClassDecorator;
my $class = Class::ClassDecorator::decorate( 'Foo::Base' => 'Foo::Bar' => 'Foo::Baz' );
my $object = $class->new;
# may be implemented in any of the three classes specified.
$object->foo();
# same thing, different internal implementation
my $class = Class::ClassDecorator::hierarchy( 'Foo::Base' => 'Foo::Bar' => 'Foo::Baz' );
DESCRIPTION
This module helps you use classes as decorators for other classes.
It provides some syntactic sugar for dynamically constructing a unique subclass which exists solely to represent a set of decorations to a base class.
This is useful when you have a base module and you want to add different behaviors (decorations) to it at a class level, as opposed to decorating a single object.
DECORATION VIA NEXT.pm
Given a base class of Foo
, and possible decorating classes Foo::WithCache
, Foo::Persistent
, and Foo::Oversize
, we could construct new classes that used any possible combination of the decorating classes.
With regular inheritance, we'd have to create many classes like Foo::PersisentWithCache
and Foo::OversizePersistent
and so on. Plus we'd still need to call methods as NEXT::foo()
from within the decorating classes or risk breaking another decorating class's behavior, because it expects to override certain methods.
With NEXT.pm
, we can easily implement our desired behavior by creating a single subclass that inherits from all of the decorating classes and the base class.
So to implement a Foo
subclass that incorporate persistence and caching, we could create a hierarchy like:
Foo::Persistent Foo::WithCache Foo
\ | /
\ | /
our subclass here
This module automates the creation of that subclass.
DECORATION AS SUBCLASSES
However, sometimes the above diagram creates a problem. The NEXT.pm
module does a depth-first, left to right search for methods. If, in our Foo
example above, the Foo
and Foo::Persistent
classes both inherited from some other class, like Bar
, we'd end up with this diagram:
Bar
/ \
/ \
/ \
Foo::Persistent Foo::WithCache Foo
\ | /
\ | /
our subclass here
This means that we call a method implemented in Bar
, like new()
on our subclass, it will get called multiple times, and the first time will immediately be after Foo::Persistent->new
calls $self->NEXT::new()
.
This may cause problems, so it may be preferable to create an actual hierarchy, or the appearance of one, that looks something like this:
Bar
|
Foo
|
Foo::WithCache
|
Foo::Persistent
|
our subclass here
In this case, we would have Foo
call $self->SUPER::new()
(using Perl's real SUPER::
dispatch) from the Foo class's new()
method. This guarantees that Bar->new()
is called once, and that it happens after Foo->new()
is called.
To do that, call the hierarchy()
method instead of decorate()
. Classes that expect to decorate other classes in this manner should use super::foo()
to call their "parent's" foo()
method. In actuality, Foo::WithCache
does not inherit from Foo
, because this module does not touch @ISA
.
USAGE
Simply call the decorate()
or hierarchy()
function with a list of classes, starting with the base class you want to decorate, followed by each decorator. The function returns a string containing the new class name.
The created classes are cached, so multiple calls with the same arguments always return the same subclass name.
The order of the arguments is significant. Methods are searched for in last to first order, so that the base class is called last. With our "persistent caching Foo" example from the DESCRIPTION, we can pretend that we have created a hierarchy like this:
Foo
|
Foo::WithCache
|
Foo::Persistent
|
our subclass here
DECORATING CLASS COOPERATION
Decorating classes must always use NEXT::
or super::
to call methods for classes "above" them in the (fictional) hierarchy, rather than SUPER::
.
Decorating classes must not actually inherit from the base class. They are, of course, free to inherit from other classes if they wish, but the author should take care in the use of NEXT::
/super::
versus SUPER::
here.
SUPPORT
Nag the author via email at autarch@urth.org.
AUTHOR
Dave Rolsky <autarch@urth.org>
Thanks to Ken Fox for suggesting this implementation.
COPYRIGHT
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the LICENSE file included with this module.