Structural

Proxy

Purpose

Provide a placeholder (surrogate) for another object in order to control access to it.

UML

Behavior

A client interfaces only with the proxy, which in turn forwards all requests to the real object when appropriate.

Example

An e-commerce application models the concept of a customer order using a class of type Order. It is required that access to the Order class be controlled based on the customer's access rights; registered customers may place an order (using the Order object), whereas guests may not place an order. 

Using  a protection proxy that controls access to the Order object, we can keep  the Order object simple by moving all access implementation details into the proxy. Customers interact only with the proxy, and based on their security credential are either allowed to place an order, or an exception of type AccessDenied is thrown.    

Usage

/* The abstract class defines the common interface to be shared by the real object and its proxy. This enables a proxy to be used wherever the real object is used In this example, the abstract class represents an Order for a commercial product */
class OrderBase
{
// Constructors/Destructor
public:
    OrderBase(){}
    virtual ~OrderBase(){}

// Public interface
public:
    virtual bool SubmitOrder(long lClientID, long lProductCode, long lQuantity) = 0;
    virtual bool AdjustOrder(long lClientID, long lOrderNo) = 0;

    // ...
};

/* This is the real object that the proxy represents */
class Order : public OrderBase
{
// Constructors/Destructor
public:
    Order(){}
    ~Order(){}

// Public interface
public:
    bool SubmitOrder(long lClientID, long lProductCode, long lQuantity)
    {
        std::cout << "Order::SubmitOrder" << std::endl;

        // Place order in database ...

        return true;
    } 

    bool AdjustOrder(long lClientID, long lOrderNo)
    {
        std::cout << "Order::AdjustOrder" << std::endl;

        // Adjust order in database ...

        return true;
    }
};

/* Proxy class: provides an interface identical to the real object so that the proxy object can be substituted for the real object. Note how the proxy maintains a reference to the real object, in addition to controlling access to it. The proxy is also responsible for creating/destroying the real object. */
class OrderProxy : public OrderBase
{
// Constructors/Destructor
public:
    // On construction, the proxy does not point to an Order object (uses lazy evaluation)
    OrderProxy() : pOrder( 0 ) {}
    ~OrderProxy(){ delete pOrder; pOrder = 0;}

// Public interface
public:
    // Returns a single instance of the order object
    Order* GetOrder()
    {
        // Create the real order object
        if (0 == pOrder)
            pOrder = new Order;

        return pOrder;
    }

    bool SubmitOrder(long lClientID, long lProductCode, long lQuantity)
    {
        // Check access rights
       
if ( !CanSubmitOrder( lClientID ))
            throw AccessDenied("Client has a CIA chip installed in his brain");

        // Assess Granted. Pass call on to the real object
        return  GetOrder()->SubmitOrder( lClientID, lProductCode, lQuantity);
    }

    bool AdjustOrder(long lClientID, long lOrderNo)
    {
        // Check access rights
       
if ( !CanAdjustOrder( lClientID ))
            throw AccessDenied("Client is mysterious");

        return  GetOrder()->AdjustOrder( lClientID, lOrderNo );
    }

// Data members
private:
    Order *pOrder;        // the real object represented by this proxy

// Helpers
private:
    bool CanSubmitOrder( long lClientID )
    {
        // Perform some meaningful checks, perhaps from the database. Hard coded for simplicity.
        if (666 == lClientID)
            return false;
        else
            return true;
    }

    bool CanAdjustOrder( long lClientID )
    {
        // Perform some meaningful checks, perhaps from the database. Hard coded for simplicity.
        if (33 == lClientID)
            return false;
        else
            return true;
    }
};

/* Exception class. Thrown by proxy class when access is denied */
class AccessDenied
{
private:
    std::string strReason;
public:
    explicit AccessDenied( const std::string& str = "" ) : strReason( str ) {}

    const std::string& Reason() const
    { 
        return strReason;
    }
};

/* Main program */

int main(int argc, char* argv[])
{
    // Attempt to place an order for ClientID 100
    {
        try
        {
            // Proxy checks security credentials prior to forwarding the call to the real object 
            OrderProxy prxOrder;
            prxOrder.SubmitOrder( 100, 12345678, 1 );                     // passes security
        }
        catch( const AccessDenied & eAD )
        {
            std::cout << eAD.Reason() << std::endl;
        }
    }

    // Attempt to adjust an order for ClientID 33
    {
        try
        {
            // Proxy checks security credentials prior to forwarding the call to the real object 
            OrderProxy prxOrder;
            prxOrder.AdjustOrder( 33 /*Client ID*/, 1234 /*Order No*/);    // fails security
        }
        catch( const AccessDenied & eAD )
        {
            std::cout << eAD.Reason() << std::endl;
        }
    }

    return 0;
}

Notes