surrogate.ss: Proxy-like Design Pattern
To load: (require (lib "surrogate.ss"))
This library provides an abstraction for building an instance of the proxy design pattern. The pattern consists of two objects, a host and a surrogate object. The host object delegates method calls to its surrogate object. Each host has a dynamically assigned surrogate, so an object can completely change its behavior merely by changing the surrogate.
The library provides a form, surrogate
:
(surrogate
method-spec ...
)
SYNTAX
where
method-spec ::== (method-name arg-spec ...) | (override method-name arg-spec ...) | (override-final method-name (lambda () default-expr) arg-spec ...) arg-spec ::== | (id ...) | id
If neither override
nor override-final
is specified
for a method-name
, then override
is assumed.
Use override
The surrogate form produces four values: a host mixin (a procedure that accepts and returns a class), a host interface, a surrogate class, and a surrogate interface, in that order.
The host mixin adds one additional
field, surrogate
, to its argument and a getter
method, get-surrogate
, and a setter
method, set-surrogate
, for changing the
field. The set-surrogate
form accepts instances the
class returned by the form or #f
, and updates the
field with its argument. Then, it calls the
on-disable-surrogate
on the previous value of the
field and on-enable-surrogate
for the new value of
the field. The get-surrogate
method returns the
current value of the field.
The host mixin has a single overriding method for each
method-name
in the surrogate
form. Each of
these methods is defined with a case-lambda with one arm for
each
arg-spec
. Each arm has the variables as arguments
in the arg-spec
. The body of each method tests the
surrogate
field. If it is #f
, the method
just returns the result of invoking the super or inner method. If the
surrogate
field is not #f
, the
corresponding method of the object in the field is invoked. This
method receives the same arguments as the original method, plus two
extras. The extra arguments come at the beginning of the argument
list. The first is the original object. The second is a procedure that
calls the super or inner method (i.e., the method of the class
that is passed to the mixin or an extension, or the method in an
overriding class), with the arguments that the procedure receives.
The host interface has the names set-surrogate
,
get-surrogate
, and each of the
method-name
s in the original form.
The surrogate class has a single public method for each
method-name
in the surrogate
form. These
methods are invoked by classes constructed by the
mixin. Each has a corresponding method signature, as
described in the above paragraph. Each method just passes
its argument along to the super procedure it receives.
Note: if you derive a class from the surrogate class, do not
both call the super
argument and the super method
of the surrogate class itself. Only call one or the other,
since the default methods call the super
argument.
Finally, the interface contains all of the names specified
in surrogate's argument, plus on-enable-surrogate
and
on-disable-surrogate
. The class returned by
surrogate
implements this interface.