Class::Proxy::Lite - Simple, lightweight object proxies


    # Make a proxy to a particular object
    $proxy = Class::Proxy::Lite->new($token, \&resolver);
    # Make a caching proxy
    $proxy = Class::Proxy::Lite->new($token, \&resolver, \$cache);

    # Methods invoked on the proxy are passed to the target object


Each instance of this class serves as a proxy to a target object. The proxy is constructed from a token and a resolver. The resolver is a code reference called with the token as its only argument; its job is to resolve the token into a reference to the desired target object.

The proxy doesn't hold a reference to its target; instead, the token must be resolved each time a method call is made on the proxy.



    $proxy = Class::Proxy::Lite->new($token, \&resolver);
    $proxy = Class::Proxy::Lite->new($token, \&resolver, \$cache);

Construct a proxy. The resolver is expected to return an object exactly equivalent (if not identical) to the desired target object. This constraint can't be formally enforced by this module, so your resolver must be written in such a way as to meet the constraint itself.

If you want one-time resolution, you may pass a reference to an undefined scalar variable as a third argument to the new method; this will be used to cache the target object the first time it's resolved, and as a result the target object won't need to be resolved again. Or you might pass a reference to a tied variable that implements caching with some sort of expiry.

(There's a lot of room for clever hacks here. For instance, you could use a resolver that returns a different object each time it's called. Also, consider passing a closure as the resolver rather than a plain old reference to a function.)

NOTE: Strictly speaking, the method new doesn't exist as such: it isn't actually defined. Instead, it's emulated using AUTOLOAD (see below) -- but only when called as a class method! This way, your target objects' class(es) can safely implement a method new that can be called as either a class method or an object method:

    $obj1 = MyClass->new(...);
    $obj2 = $obj1->new(...);

See perltoot for information on how to implement this style of constructor.

When new is called as a class method on your own class, Class::Proxy::Lite isn't involved (unless you set up your objects' classes to inherit from it, which is a very bad idea). When new is called as an object method, the call is passed on to the target object just as would happen for any other object method call.


This is where the action takes place. It simply calls the resolver to get a reference to the target object, then passes the method call on to it.

The methods DESTROY and import are special-cased; the former is ignored, while the latter is ignored if and only if it was invoked on an object (i.e., not called implicitly as the result of a use statement).

Except for import and new, all methods invoked on this class or a subclass of it (as opposed to methods invoked on an actual object) result in an exception being thrown. An exception is also thrown if the resolver returns undef or a non-reference -- in other words, if it can't resolve the token into an actual object.

WARNING: Never call AUTOLOAD directly!


Depending on your needs, it may not be necessary to subclass Class::Proxy::Lite. If you do, however, your subclass will probably look something like this:

    package MyObject::Proxy;
    @ISA = qw(Class::Proxy::Lite);
    sub new {
        my ($cls, $target) = @_;
        my $token = obj2token($target);
        my $resolver = \&token2obj;
        return $self->SUPER::new($token, $resolver);
    sub obj2token { ... }
    sub token2obj { ... }

See t/proxy.t for a slightly different example.

Class::Proxy::Lite was designed to avoid method name clashes; the only method defined for it is AUTOLOAD. If your subclass must inherit from another class that uses AUTOLOAD, this is probably not the right solution for you.


Class::Proxy didn't fit my needs. I was implementing an object model in which objects are loaded dynamically and references to loaded objects are stored in a master table. I wanted a solution that served both as a proxy and a reference (generally speaking) to an object. This module is what resulted.


Apparently, it's not possible to catch calls to the can() and isa() methods on instances of Class::Proxy::Lite. This makes it impossible to implement a true proxy without defining UNIVERSAL::isa and UNIVERSAL::can, which I'm reluctant to do.

The following note in perlobj (under `Default UNIVERSAL methods') appears to explain the problem:

   NOTE: `can' directly uses Perl's internal code for method
   lookup, and `isa' uses a very similar method and cache-ing
   strategy. This may cause strange effects if the Perl code
   dynamically changes @ISA in any package.

I might be wrong about all this, though; any insights on this problem are welcome.


Class::Proxy is a better alternative for more sophisticated proxy capabilities.




Paul Hoffman <nkuitse AT cpan DOT org>.


Thanks to Kurt Starsinic (KSTAR) for Class::Delegate, which got me thinking, and to Murat Uenalan (MUENALAN) for Class::Proxy, which set a good example.


Copyright 2003 Paul M. Hoffman. All rights reserved.

This program is free software; you can redistribute it and modify it under the same terms as Perl itself.