Proxy design pattern

Proxy design pattern #

Name: Proxy Type: Structural

Intent: #

The Proxy design pattern is a structural pattern that provides an in-between, or placeholder for another object. They are closely related in structure, to Adapters and Decorators but they have different purposes.

When to use #

The Proxy design pattern can be used in a variety of scenarios, but its goal is generally to preserve exactly (or as closely as possible) the API that is being used while offering certain internal enhancements.

It's useful when we want to add a layer of control over the interaction with an object. In the context of C#, implementing the Proxy pattern involves creating a proxy class that wraps an object and controls access to it.

Real-life analogy: #

Imagine you are at a restaurant that has a limited number of tables. The restaurant has a hostess who stands at the door and checks if there is a table available before seating a customer. The hostess acts as a proxy for the tables. She controls access to the tables and chairs and ensures that the tables are distributed as best as possible.

Another example we often encounter in real life is when we order coffee. When we want to order a coffee, we don't go directly to the barista behind the counter or make it ourselves. Instead, we place the order with a person, who then relays the order to the barista. The person taking our order acts as a proxy for us, handling the communication and coordination between us and the barista.

Structure: #

For the proxy design pattern, we have three main entities involved.

  • One is the abstraction which is usually an interface that defines the operations
  • The real Subject - a class that implements the interface and usually has more operations than is specified there
  • The Proxy acts as an intermediary between the Client and the Real Subject. This too implements the interface and has-a Real Subject instance. It is the common ground between the Client(consumer) and the Subject, providing an indirection layer.
  • The client is the one interacting with the Proxy

You can see in the diagram below the interaction between classes.

Implementation #

For our example implementation we have the following components:

  • The IOrderService interface represents the service interface for placing food orders.
  • The RestaurantKitchen class is the real subject that prepares the food. This is the actual code that we want to interact with.
  • The Waiter class acts as a proxy for the RestaurantKitchen. When a client wants to place an order, the waiter can perform various tasks (like confirming the order, checking special requests, etc.) before delegating the actual food preparation to the RestaurantKitchen.
  • The client interacts with the IOrderService interface and uses the Waiter to place orders. The client is unaware of the direct interaction with the RestaurantKitchen which illustrates the encapsulation provided by the Proxy pattern.

This design allows for additional logic and features to be implemented in the Waiter without modifying the underlying RestaurantKitchen or the client's code.

// Service Interface
public interface IOrderService
{
    void PlaceOrder(string dish);
}

// Real Subject
public class RestaurantKitchen : IOrderService
{
    public void PlaceOrder(string dish)
    {
        Console.WriteLine($"Cooking {dish} in the kitchen.");
    }
}

  public class Waiter : IOrderService
  {
      private RestaurantKitchen _kitchen;

      public Waiter(RestaurantKitchen kitchen)
      {
          _kitchen = kitchen;
      }

      public void PlaceOrder(string dish)
      {
          Console.WriteLine($"Waiter is taking order for {dish}.");
          // Additional checks or preparations can be done here.
          _kitchen.PlaceOrder(dish);
      }
  }

Pros & Cons #

Pro #

  1. Control Access: The proxy can control access to the real subject(RestaurantKitchen), which is useful for lazy initialization, permissions, etc.

  2. Additional Functionality: You can add additional functionality (e.g., caching, logging, monitoring) before/after the request to the real subject.

  3. Open/Closed Principle: You can introduce new proxies without changing the subject or client

CONS: #

  • Code Complexity: Code may become complicated, if both the real subject and proxy implement the same interface.
  • Response Time: The proxy can add a delay to the response time due to additional processing.
  • adding a layer of indirection

The Proxy Design Pattern stands out as it does not conform to a single, uniform structure. There is a wide variety of proxy types that serve distinct functions. The choice of which one to implement depends on the specific requirements of the software design problem at hand. Below there are just a few of them:

Proxy types: #

  1. Virtual Proxy: This type of proxy creates expensive objects on demand. The proxy contains a placeholder for an object that is extremely resource-intensive to create. The actual object is only created when its functionality is absolutely needed. This is useful in scenarios like lazy initialization of a large image or a file.
  2. Remote Proxy: This proxy provides a local representative for an object that resides in a different address space (like another computer or server). It's used in distributed systems to hide the details of remote interaction. The proxy takes care of the details of working with remote methods, such as handling network communication and marshaling.
  3. Protective Proxy: This proxy controls access to a sensitive master object. It's used to manage the different access rights of clients and to protect the target object from unauthorized access. For example, it might check if a client has the necessary permissions to perform an action.
  4. Smart Proxy: A smart proxy adds extra layers of functionality when an object is accessed. This can include additional steps when an object is accessed, such as reference counting, logging, or locking to manage concurrent access to an object. It can also perform tasks that are needed for housekeeping purposes, like freeing resources or initializing objects upon access.
  5. Caching Proxy: This proxy can store recent or frequent requests and provide cached responses to reduce the operation's time or complexity. It's commonly used in systems where fetching or calculating the original data is very resource-intensive, like in web content delivery networks.

Each of these proxy types serves a different purpose, and the choice of which one to implement depends on the specific requirements of the software design problem at hand.

Summary #

In essence, the Proxy design pattern is a powerful tool that can take many forms. It doesn’t try to expand the public API surface of an object by adding new members but it can control access and add supplementary behavior to objects without changing the object's code or the client's code.

P.S. Want the source code or level up your coding? Consider joining my monthly newsletter. Sign up here!