Design patterns are reusable solutions to common software design problems. In my previous posts, I’ve covered patterns like Factory Method, Builder, and Chain of Responsibility. Today, I’ll explain the Proxy Pattern, another important Gang of Four (GoF) structural design pattern.
Github link to the example 👉 https://github.com/Prasadct/proxy-pattern
What is the Proxy Pattern?
The Proxy pattern provides a surrogate or placeholder for another object to control access to it. It’s like having a representative who acts on behalf of the real object. This pattern is useful when:
- You want to control access to an object
- You need to add functionality when accessing an object
- You want to delay the creation and initialization of an expensive object until it’s actually needed
Types of Proxies
There are several types of proxies, each serving different purposes:
- Virtual Proxy: Creates expensive objects on demand
- Protection Proxy: Controls access to the original object’s methods
- Remote Proxy: Represents an object that exists in a different address space
- Logging Proxy: Keeps a log of method calls to the object
- Caching Proxy: Stores results of expensive operations for reuse
Structure of the Proxy Pattern
The Proxy pattern consists of these components:
- Subject Interface: Defines the common interface for RealSubject and Proxy
- RealSubject: The real object that the proxy represents
- Proxy: Maintains a reference to the RealSubject and controls access to it
Here’s a diagram showing the structure:
Real-World Example: API Rate Limiter Proxy
Let’s implement a real-world example of the Proxy pattern: an API rate limiter. This is a common requirement in web applications to prevent abuse and ensure fair usage.
Imagine you have a weather service that provides weather data. Without rate limiting, users could potentially overwhelm your service with requests. A rate limiter proxy can control how many requests a user can make within a given time period.
Step 1: Define the Subject Interface
First, let’s define our subject interface for the weather service:
Step 2: Implement the Real Subject
Next, we’ll implement the actual weather service:
Step 3: Implement the Proxy
Now, let’s create our rate limiter proxy:
Step 4: Client Code Using the Proxy
Here’s how a client would use our rate-limited weather service:
The output would look something like:
Fetching weather data for Bangalore from external API
First request: Temperature: 25°C, Condition: Sunny, Location: Bangalore
Fetching weather data for Mumbai from external API
Second request: Temperature: 25°C, Condition: Sunny, Location: Mumbai
Fetching weather data for Delhi from external API
Third request: Temperature: 25°C, Condition: Sunny, Location: Delhi
Error: Rate limit exceeded. Try again later.
Sequence of Events
Let’s look at a sequence diagram to understand how the Proxy pattern works in our example:
Another Example: Image Loading Proxy
Another classic example of the Proxy pattern is lazy loading of heavy resources like images. Let’s quickly look at a Virtual Proxy implementation for image loading:
Output:
Images will be loaded on demand...
Displaying first image:
Loading image: image1.jpg
Displaying image: image1.jpg
Displaying first image again:
Displaying image: image1.jpg
Displaying second image:
Loading image: image2.jpg
Displaying image: image2.jpg
When to Use the Proxy Pattern
Use the Proxy pattern when:
- You need lazy initialization (Virtual Proxy)
- You need access control to an object (Protection Proxy)
- You need to add logging, caching, or other functionality when accessing an object
- You want to control resources that are expensive to create or use
Advantages of the Proxy Pattern
Separation of Concerns: The proxy handles additional functionality while the real subject focuses on its primary responsibility
Open/Closed Principle: You can introduce new proxies without changing the real subject
Protection: Controls access to the real subject
Improved Performance: Can implement caching or lazy loading to optimize resource usage
Disadvantages of the Proxy Pattern
Increased Complexity: Adds another layer of indirection
Response Time: May increase response time in some cases
Implementation Overhead: Requires creating additional classes
Proxy Pattern vs. Other Patterns
Decorator vs. Proxy: Both wrap an object, but decorators add responsibilities while proxies control access
Adapter vs. Proxy: Adapters change an interface, while proxies keep the same interface
Facade vs. Proxy: Facades simplify a complex system, while proxies control access to objects
Conclusion
The Proxy pattern is a powerful tool for controlling access to objects. It can help you implement various functionalities like lazy loading, caching, access control, and logging without modifying the original object. In our real-world examples, we saw how proxies can be used for rate limiting API calls and lazy loading images.
As with all design patterns, use the Proxy pattern when it clearly solves your specific problem, not just because it’s available. Consider the performance implications and added complexity before implementing this pattern in your code.
Have you used the Proxy pattern in your projects? Share your experiences in the comments below!
Awesome
Good
Good
Awesome