Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've spent a lot of time reviewing code from junior engineers, and there is one mistake that pops up almost every time we hit the Proxy pattern: the belief that a Proxy is just a Decorator by another name. If you've already studied the Decorator pattern, you know it's all about wrapping an object to add behavior. You might look at a Proxy—which also wraps an object—and think, "Why do I need a new name for this?"

"Proxies are just Decorators with different names"

Here is why that thinking will lead you astray. A Decorator's job is to enhance the object. If I have a Coffee object, a Decorator adds Milk or Sugar. The coffee still exists, and the Decorator just adds a layer of flavor on top. The client usually provides the original object to the Decorator.

A Proxy, however, is about control. The Proxy doesn't just add "flavor"; it manages the lifecycle of the real object. In many cases, the Proxy creates the real object itself, or decides if the real object should even be accessed at all. If the Decorator is a spice rack, the Proxy is a bouncer at a club. The bouncer isn't "enhancing" the club; he's deciding if you're allowed inside.

Controlling Access and Resource Management

To see this in action, let's look at a scenario I've dealt with often: loading massive resources. Imagine you're building a gallery app that displays high-resolution 4K images. If you load 100 of these into memory the moment the app starts, your JVM is going to crash with an OutOfMemoryError before the user even sees the first screen.

This is where a Virtual Proxy comes in. Instead of the client holding the actual RealImage, they hold a ProxyImage. The real image isn't loaded until the very moment display() is called.

public interface Image {
    void display();
}

public class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(); // This is the expensive part
    }

    private void loadFromDisk() {
        System.out.println("Loading heavy image: " + fileName);
    }

    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }
}

public class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        // Lazy Initialization: We only create the real object when absolutely necessary
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

Notice the key difference here. In a Decorator, you'd likely pass an existing Image into the ProxyImage constructor. Here, the ProxyImage owns the creation logic. The client doesn't even know RealImage exists; they just interact with the Image interface.

Different Flavors of Control

While the "Virtual Proxy" (lazy loading) is the most common, I want you to keep two other types in your toolkit:

  • Protection Proxy: This is the "bouncer" I mentioned. It checks if the current user has the required permissions before calling the method on the real object.
  • Remote Proxy: This is what happens under the hood with RMI or gRPC. The proxy represents an object that actually lives on a different server, handling all the networking "magic" so the client thinks they're just calling a local method.

When you're deciding between these patterns, ask yourself: "Am I adding features to an object (Decorator), or am I managing how an object is accessed (Proxy)?" If the answer is access, you've got a Proxy.




📋 Practical Task

Build a Secure Document Access Proxy

You need to implement a system where certain documents can only be read by users with "ADMIN" privileges. Create the following structure:

  • An IDocument interface with a method String getContent().
  • A SensitiveDocument class that implements IDocument and returns a secret string (e.g., "Top Secret Government Data").
  • A DocumentProxy class that implements IDocument. This proxy should:
    • Take a String userRole in its constructor.
    • Maintain a reference to a SensitiveDocument.
    • In getContent(), check if the userRole is equal to "ADMIN".
    • If it is, return the content from the real document.
    • If it is not, return "Access Denied: Insufficient Permissions".

Test your implementation by creating one proxy with the role "USER" and another with "ADMIN", and verify that only the admin can see the secret content.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.