Provide a placeholder (surrogate) for another object in order to control access to it.
A client interfaces only with the proxy, which in turn forwards all requests to the real object when appropriate.
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.
/* 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;
}