Proxy

Intent

Problem

You need to support resource-hungry objects, and you do not want to instantiate such objects unless and until they are actually requested by the client.

Structure Summary

Structure category:  
wrapper
Similar patterns:   Adapter   Facade

Discussion

Design a surrogate, or proxy, object that: instantiates the real object the first time the client makes a request of the proxy, remembers the identity of this real object, and forwards the instigating request to this real object. Then all subsequent requests are simply forwarded directly to the encapsulated real object.

There are four common situations in which the Proxy pattern is applicable.

  • A virtual proxy is a placeholder for "expensive to create" objects. The real object is only created when a client first requests/accesses the object.

  • A remote proxy provides a local representative for an object that resides in a different address space. This is what the "stub" code in RPC and CORBA provides.

  • A protective proxy controls access to a sensitive master object. The "surrogate" object checks that the caller has the access permissions required prior to forwarding the request.

  • A smart proxy interposes additional actions when an object is accessed. Typical uses include:

    Structure

    By defining a Subject interface, the presence of the Proxy object standing in place of the RealSubject is transparent to the client.

    The Proxy is a "wrapper" that delegates to the real object.

    Example

    The Proxy provides a surrogate or place holder to provide access to an object. A check or bank draft is a proxy for funds in an account. A check can be used in place of cash for making purchases and ultimately controls access to cash in the issuer's account. [Michael Duell, "Non-software examples of software design patterns", Object Magazine, Jul 97, p54]

    Check list

    1. Identify the leverage or “aspect” that is best implemented as a wrapper or surrogate.
    2. Define an interface that will make the proxy and the original component interchangeable.
    3. Consider defining a Factory that can encapsulate the decision of whether a proxy or original object is desirable.
    4. The wrapper class holds a pointer to the real class and implements the interface.
    5. The pointer may be initialized at construction, or on first use.
    6. Each wrapper method contributes its leverage, and delegates to the wrappee object.

    Before and after

    BeforeAfter
    // Direct coupling, lots of start-up and shut-down
    // overhead
    
    class Image {
       int        m_id;
       static int s_next;
    public:
       Image() { m_id = s_next++;
          cout << "   $$ ctor: "<< m_id << '\n'; }
       ~Image() {
          cout << "   dtor: " << m_id << '\n'; }
       void draw() {
          cout << "   drawing image " << m_id << '\n'; }
    };
    int Image::s_next = 1;
    
    int main( void ) {
       Image images[5];
    
       for (int i; true; ) {
          cout << "Exit[0], Image[1-5]: ";
          cin >> i;
          if (i == 0)
             break;
          images[i-1].draw();
    }  }
    
    //    $$ ctor: 1
    //    $$ ctor: 2
    //    $$ ctor: 3
    //    $$ ctor: 4
    //    $$ ctor: 5
    // Exit[0], Image[1-5]: 2
    //    drawing image 2
    // Exit[0], Image[1-5]: 4
    //    drawing image 4
    // Exit[0], Image[1-5]: 2
    //    drawing image 2
    // Exit[0], Image[1-5]: 0
    //    dtor: 5
    //    dtor: 4
    //    dtor: 3
    //    dtor: 2
    //    dtor: 1
    
      
    // Initialization on first use
    
    // 1. Design an "extra level of indirection" wrapper class
    // 2. The wrapper class holds a pointer to the real class
    // 3. The pointer is initialized to null
    // 4. When a request comes in, the real object is created
    //    "on first use" (aka lazy intialization)
    // 5. The request is always delegated
    
    class RealImage {
       int  m_id;
    public:
       RealImage( int i ) { m_id = i;
          cout << "   $$ ctor: "<< m_id << '\n'; }
       ~RealImage() {
          cout << "   dtor: " << m_id << '\n'; }
       void draw() {
          cout << "   drawing image " << m_id << '\n'; }
    };
    
    // 1. Design an "extra level of indirection" wrapper class
    class Image {
       // 2. The wrapper class holds a pointer to the real class
       RealImage* m_the_real_thing;
       int        m_id;
       static int s_next;
    public:
       Image()  {
          m_id = s_next++;
          // 3. Initialized to null
          m_the_real_thing = 0;
       }
       ~Image() { delete m_the_real_thing; }
       void draw() {
          // 4. When a request comes in, the real object is
          //    created "on first use"
          if ( ! m_the_real_thing)
             m_the_real_thing = new RealImage( m_id );
          // 5. The request is always delegated
          m_the_real_thing->draw();
    }  };
    int Image::s_next = 1;
    
    int main( void ) {
       Image images[5];
    
       for (int i; true; ) {
          cout << "Exit[0], Image[1-5]: ";
          cin >> i;
          if (i == 0)
             break;
          images[i-1].draw();
    }  }
    
    // Exit[0], Image[1-5]: 2
    //    $$ ctor: 2
    //    drawing image 2
    // Exit[0], Image[1-5]: 4
    //    $$ ctor: 4
    //    drawing image 4
    // Exit[0], Image[1-5]: 2
    //    drawing image 2
    // Exit[0], Image[1-5]: 0
    //    dtor: 4
    //    dtor: 2
    

    Rules of thumb

    Adapter provides a different interface to its subject. Proxy provides the same interface. Decorator provides an enhanced interface. [GoF. p216]

    Decorator and Proxy have different purposes but similar structures. Both describe how to provide a level of indirection to another object, and the implementations keep a reference to the object to which they forward requests. [GoF, p220]