Delegation
is a technique whereby an object forwards method calls to an
appointed delegate object. In the following
example, an Employee
class simply delegates all
tax-related functionality to the $acccounting_dept
object:
package Employee; sub compute_after_tax_income { $me = $_[0]; return $accounting_dept->compute_after_tax_income($me); }
There are cases in which you want all method calls that are not
handled by a class to be automatically forwarded to a delegate. This
is a cinch in Perl, since the AUTOLOAD
function is
called when a procedure is not found within that package or its base
classes:
package Employee; sub AUTOLOAD { my $obj = $_[0]; # $AUTOLOAD contains the name of the missing method # Never propagate DESTROY methods return if $AUTOLOAD =~ /::DESTROY$/; # Strip off its leading package name (such as Employee::) $AUTOLOAD =~ s/^.*:://; $obj->{delegate}->$AUTOLOAD(@_); # Note, $obj is still part of @_, # so the delegated function knows # the original target }
Notice that AUTOLOAD
is called if
DESTROY
is not defined, and it is important that
you not forward that message, or the delegate will think Perl is
about to destroy it and release its resources prematurely.
This technique is often employed in the guts of client/server libraries. In a typical client/server system, the server has the “real” objects. But the system is written in such a way that a client can remotely invoke a method of the object, with familiar OO syntax. For example, if a client program wants to invoke a method on a remote bank account, it should be able to say something like this:
$account->deposit(100); # Deposit 100 bucks.
On the surface, it seems like an ordinary method call. What the
library hides from you is that the deposit()
functionality is actually sitting on a different machine. How is this
accomplished? Well, the $account
object reference
is actually a reference to a lightweight proxy
object on the client side. Its sole purpose is to forward calls to
the remote machine (by sending messages over a socket, for example)
and to wait for the response to come back. In other words, the
account
object is not the real account. It is only
a message forwarder. It delegates its functionality to the remote
object with the help of the messaging system underneath.