The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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.