The Proxy design pattern and Aspect Oriented Programming have the common goal of extracting cross cutting concerns from code and encapsulating them. A cross cutting concern usually happens on a function boundary: check security, object creation and so on. Proxies allow you to make an object that mimics the interface of the called object, but which provides additional functionality.
For an inversion of control container, object dependency and object creation may follow two different policies. If Object A needs and Object of type B, that dependency should be initialized when object A is created.. However, if creating object B is expensive, and object B is not always needed, object B should be created on Demand. This approach is called “Lazy Load” and it is one of the types of proxies that the Gang of Four book enumerates.
Java provides a mechanism to make a proxy on the fly. The use of the proxy object provides a function
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
Let’s define a C++ class as a pure abstract base class:
class Interface {
public:
virtual void action1(int i) = 0;
virtual void action2(int j) = 0;
}
And a class that implements that interface with some side effect.
class RealClass :public Interface {
int val;
public:
void action1(int i){val = i;}
void action2(int i){val = 333 * i;}
};
Then a Lazy Load Proxy would be defined like this:
typedef Interface* (* create_delegate_fn());
class LazyLoadProxy : public Interface {
create_delegate_fn* fetcher;
Interface* delegate;
Interface* fetch(){
if (!delegate){
delegate = (*fetcher());
}
return delegate;
}
public:
LazyLoadProxy(create_delegate_fn create_delegate):
delegate(0)
{
fetcher = create_delegate;
};virtual void action1(int i){
fetch()->action1(i);
};
virtual void action2(int j){
fetch()->action1(j);
};
}
This cannot be completely templatized, but a good portion of it can be abstracted away, leaving the compiler to check your work for the rest.  If we want to tie this into out inversion of control framework, we need to make sure that the create_delegate has access to the same Zone used to create the Proxy object. Thus the Zone should be stored in a member variable of the Dynamic proxy. We should really tie this into the resolver.h code from previous posts, and pass the Zone along to be stored the lazy load proxy. It is also likely that you will want the lazy load proxy to own the delegated item, so you may want to add a virtual destructor to the interface (always a good idea), and then delete the delegate in the destructor of the proxy. Here’s the templatized code:
#include <resolver.h>
template <typename T>Â class LazyLoadProxy : public TÂ {
public:
typedef T* (*create_delegate_fn)(dependency::Zone&);private:
T* (*fetcher)(dependency::Zone&);
T* delegate;
dependency::Zone& zone_;protected:
T* fetch(){
if (!delegate){
delegate = (fetcher(zone_));
}
return delegate;
}
public:
LazyLoadProxy(dependency::Zone& zone,create_delegate_fn create_delegate):
zone_(zone),
delegate(0)
{
fetcher = create_delegate;
};virtual ~LazyLoadProxy(){
if (delegate){
delete delegate;
}
}
};
And the code specific to creating and registering the Interface version of the LazyLoadProxy is:
class InterfaceLazy : public LazyLoadProxy<Interface>Â {
public:
InterfaceLazy(dependency::Zone& zone, create_delegate_fn create_delegate):
LazyLoadProxy<Interface>(zone, create_delegate)
{
};virtual void action1(int i){
fetch()->action1(i);
};
virtual void action2(int j){
fetch()->action1(j);
};
};static Interface* createReal(dependency::Zone& zone){
return new RealClass;
}static Interface* createProxy(dependency::Zone& zone){
return new InterfaceLazy(zone, createReal);
}DEPENDENCY_INITIALIZATION{
dependency::supply<Interface>::configure(0,createProxy);
return true;
}
Java dynamic proxies reduce the code for the proxy down to a singe function that gets executed for each method on the public interface, with the assumption that any delegation will be done via the reflection API. C++ Does not have a reflection API, so we can’t take that approach. If the C++ language were extended to allow the introspection of classes passed to a template, we could build a similar approach at compile time by providing a simple template function that gets expanded for each method of the abstract interface.
Dynamic proxies that are parameter agnositc are possible in C++, but are architecture specific, and depend on the parameter passing convetion. I’m looking in to this, and will publish what I find in a future article.