The Proxy pattern provides a surrogate or placeholder object to control access to another, different object. The Proxy object can be used in the same manner as its containing object.
The Proxy object can then hide or change data on the hidden object, or otherwise manipulate its behavior. However, the Proxy must still be able to be used anywhere the hidden object is.
NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in this series!
The Rundown
- Type: Structural
- Useful? 4/5 (Very)
- Good For: Controlling access to a particular object, testing scenarios.
- Example Code: On GitHub
The Participants
- The Subject defines a common interface for the RealSubject and the Proxy such that the Proxy can be used anywhere the RealSubject is expected.
- The RealSubject defines the concrete object which the Proxy represents.
- The Proxy maintains a reference to the RealSubject and controls access to it. It must implement the same interface as the RealSubject so that the two can be used interchangeably.
A Delicious Example
To demonstrate how to use the Proxy design pattern in real-world code, let's talk about servers in a high-end restaurant (as we did for the Facade pattern and the Adapter pattern).
For this demo, let's imagine that servers at a restaurant primarily do three things:
- Take the patron's order.
- Deliver the patron's order.
- Process the patron's payment.
With these assumptions, we can create an interface IServer
for these actions (this interface is the Subject participant):
/// <summary>
/// The Subject interface which both the RealSubject and proxy will need to implement
/// </summary>
public interface IServer
{
void TakeOrder(string order);
string DeliverOrder();
void ProcessPayment(string payment);
}
Now let's create a real Server
class (the RealSubject participant):
/// <summary>
/// The RealSubject class which the Proxy can stand in for
/// </summary>
class Server : IServer
{
private string Order;
public void TakeOrder(string order)
{
Console.WriteLine("Server takes order for " + order + ".");
Order = order;
}
public string DeliverOrder()
{
return Order;
}
public void ProcessPayment(string payment)
{
Console.WriteLine("Payment for order (" + payment + ") processed.");
}
}
Now imagine that our Server
is an experienced server who is helping train a newly-employed server. That new employee, from the patron's perspective, is still a server and should still behave as such. However, the new trainee cannot process payments yet, as he must first learn the ropes of taking and delivering orders.
We can create a Proxy to model this new employee. The Proxy will need to maintain a reference back to the Server
instance so that it can call the Server
instance's ProcessPayment()
method:
/// <summary>
/// The Proxy class, which can substitute for the Real Subject.
/// </summary>
class NewServerProxy : IServer
{
private string Order;
private Server _server = new Server();
public void TakeOrder(string order)
{
Console.WriteLine("New trainee server takes order for " + order + ".");
Order = order;
}
public string DeliverOrder()
{
return Order;
}
public void ProcessPayment(string payment)
{
Console.WriteLine("New trainee cannot process payments yet!")
_server.ProcessPayment(payment);
}
}
As you can see, the NewServerProxy
implements its own TakeOrder()
and DeliverOrder()
methods, and calls the Server
class's ProcessPayment()
method. Since they both implement IServer
, the NewServerProxy
can be used any place the Server
can be used.
Will I Ever Use This Pattern?
Probably. If you've ever had a need to change the behavior of an existing object without actually changing the definition of that object, the Proxy pattern can allow you to do that. Further, I can see this being very useful in testing scenarios, where you might need to replicate a class's behavior without fully implementing it.
Summary
The Proxy pattern seeks to create a "stand-in" object which can be used in place of an existing object and maintains a reference to an instance of said existing object. To fulfill the pattern, the Proxy object must be able to be used anywhere the replaced object can be used.
And, while you're here, check out our wine specials. There's sure to be something to suit your taste.
Happy Coding!