Java > Design Patterns in Java > Structural Patterns > Proxy Pattern

Proxy Pattern Demonstration in Java

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. This example demonstrates a simple image proxy, which loads the real image only when it's actually needed. This can improve performance and reduce initial resource consumption.

Defining the Image Interface

This interface declares the common method `display()` that both the real image and the proxy will implement. It's the contract that both the `RealImage` and `ProxyImage` adhere to.

interface Image {
    void display();
}

Creating the Real Image Class

The `RealImage` class represents the actual image that will be displayed. The `loadFromDisk()` method simulates the process of loading the image from a file, which could be a time-consuming operation. The constructor loads the image upon instantiation.

class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk(filename);
    }

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

    private void loadFromDisk(String filename) {
        System.out.println("Loading " + filename);
    }
}

Implementing the Proxy Image Class

The `ProxyImage` class acts as a surrogate for the `RealImage`. It doesn't load the actual image until the `display()` method is called. This lazy loading mechanism improves performance. The `realImage` instance is only created when needed.

class ProxyImage implements Image {
    private String filename;
    private RealImage realImage;

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

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

Demonstration

This code demonstrates the usage of the proxy. The `ProxyImage` is created, but the `RealImage` is not loaded until the first call to `display()`. Subsequent calls to `display()` use the already loaded `RealImage`.

public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_image.jpg");

        // Image will be loaded from disk only when display is called
        image.display();
        System.out.println("");

        // Image will be displayed without loading again
        image.display();
    }
}

Concepts Behind the Snippet

This snippet demonstrates the Proxy pattern's core concept: controlling access to an object by providing a surrogate. The proxy can perform additional actions before or after the real object is accessed, such as lazy initialization, access control, or logging.

Real-Life Use Case

A common real-life use case is image loading in web applications. Instead of loading all images at once, which can slow down the page load time, you can use a proxy to load images only when they are visible in the viewport or when the user interacts with them.

Best Practices

  • Use the proxy pattern when you want to control access to an object.
  • Ensure the proxy class implements the same interface as the real object, allowing clients to use the proxy without knowing they are not directly interacting with the real object.
  • Consider the performance impact of the proxy. While it can improve initial load times, the overhead of the proxy itself should be minimal.

Interview Tip

When discussing the Proxy pattern in an interview, emphasize its ability to provide a level of indirection and control access to an object. Explain how it can be used for lazy initialization, security, and other purposes.

When to Use Them

Use the Proxy pattern when:

  • You want to implement lazy initialization.
  • You want to control access to an object for security reasons.
  • You need to add additional functionality before or after accessing an object, such as logging or caching.

Memory Footprint

The Proxy pattern can reduce the initial memory footprint by deferring the creation of the real object until it is actually needed. This is particularly useful when dealing with large objects or a large number of objects.

Alternatives

Alternatives to the Proxy pattern include:

  • Decorator pattern: Adds behavior to an object dynamically.
  • Adapter pattern: Converts the interface of a class into another interface clients expect.

Pros

  • Lazy initialization: Delays the creation of the real object until it is needed.
  • Access control: Can control access to the real object.
  • Additional functionality: Can add additional functionality before or after accessing the real object.

Cons

  • Increased complexity: Adds an extra layer of indirection, which can increase the complexity of the code.
  • Performance overhead: The proxy can introduce a slight performance overhead.

FAQ

  • What is the difference between a Proxy and a Decorator?

    A Proxy controls access to an object, while a Decorator adds behavior to an object. A Proxy has the same interface as the real object, while a Decorator can add new methods.
  • When is lazy initialization useful?

    Lazy initialization is useful when creating an object is expensive in terms of time or resources, and the object may not be needed immediately.