NailYourInterview
Structural Design Patterns

Proxy Design Pattern in Java with Real-World Example

Learn the Proxy Design Pattern in Java with a real-world example. Understand how it adds a security, caching, or control layer between client and service.

Proxy Design Pattern

Video thumbnail

The Proxy Design Pattern is a structural design pattern used when you want to add a security check, caching layer, or any extra step between a client and the actual service.

A Simple Real-Life Example

Think about your social media account.

You can:

  • View your profile.

  • Delete your own account.

  • Cannot Delete someone else’s account.

But an admin can delete anyone’s account. That’s where Proxy comes in. It sits in between the user and the system, checking who’s making the request and deciding what should happen.

Proxies are often used for:

  1. Access control

  2. Caching

  3. Rate limiting

  4. Logging

...and more!

Proxy Design Pattern Example
UML for Proxy Design Pattern

Problem Without Using Proxy Pattern

Assume that we proceed without proxy design pattern

No Plug-and-Play Behavior

If you’re not using a proxy, you can’t easily add proxies like RateLimitingProxy, LoggingProxy, CachingProxy, AccessControlProxy. You’ll have to put everything into one big service file. That creates tight coupling.

Too Many Responsibilities

When your service handles logic for role checks, caching, and core functionality — it violates the Single Responsibility Principle. Now the service is doing too much.

Difficult to Test

When everything is mixed into one file, writing clean unit tests becomes hard. You can’t test access control separately from core business logic.

Solution with Proxy Design Pattern

UML Diagram

Proxy Design Pattern UML
UML for Proxy Design Pattern

Here’s how it works:

  • The Client talks to a Proxy.

  • The Proxy checks if the user is allowed to do something.

  • If allowed, it forwards the request to the real service (UserServiceImpl).

  • Both the Proxy and the Real Service implement the same interface — UserService. This is important because it allows the client to interact with either of them in the same way. So, even if we decide to remove the Proxy later, the client code will still work without any changes. Think of it like a power socket — you can plug in or unplug the proxy anytime without breaking the setup.

Code

public class UserServiceProxy implements UserService {
    private final UserService realService;
 
    public UserServiceProxy(UserService realService) {
        this.realService = realService;
    }
 
    @Override
    public void getUser(User caller, User target) {
        // Allow all roles to get employee info
        realService.getUser(caller, target);
    }
 
    @Override
    public void deleteUser(User caller, User target) {
        if (caller.getRole() == Role.ADMIN || caller.getName().equals(target.getName())) {
            realService.deleteUser(caller, target);
        } else {
            System.out.println(caller.getName() + " is not authorized to delete user: " + target.getName());
        }
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        User admin = new User("Alice", Role.ADMIN);
        User user = new User("Bob", Role.USER);
        User targetUser = new User("John Doe", Role.USER);
 
        // Real service
        UserService realService = new UserServiceImpl();
 
        // Proxy wraps the real service
        UserService proxy = new UserServiceProxy(realService);
 
        // Admin actions
        proxy.getUser(admin, targetUser);
        proxy.deleteUser(admin, targetUser);
 
        // Normal user actions
        proxy.getUser(user, targetUser);
        proxy.deleteUser(user, targetUser);
    }
}

Key Benefits of the Proxy Design Pattern

On this page