What is a Proxy Design Pattern? How to Implement?

The proxy design pattern is a software design pattern that provides a surrogate or placeholder for another object to control access to it. A proxy object is similar to the original object, but it doesn’t have all of the same functionality. The proxy object can forward requests to the original object, but it can also perform additional actions before or after the request is forwarded.

The proxy design pattern is often used to:

  • Control access to a resource
  • Provide a layer of abstraction
  • Improve performance
  • Handle errors

Use Case Examples

Here is an example of how the proxy design pattern can be used to control access to a resource. Let’s say you have a class that represents a database connection. You want to make sure that only authorized users can access the database. You can use the proxy design pattern to create a proxy object that checks the user’s authorization before forwarding the request to the real database connection object.

Proxy Design Pattern

Here is another example of how the proxy design pattern can be used. Let’s say you have a class that represents a large image file. You want to improve the performance of your application by loading the image file only when it is needed. You can use the proxy design pattern to create a proxy object that loads the image file when it is first requested. The proxy object will then cache the image file so that it can be accessed quickly when it is requested again.

Code examples

Here is an example of a Java program that uses the proxy pattern to create a proxy object that represents a remote object:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {

    String sayHello() throws RemoteException;
}

public class HelloImpl implements Hello {

    @Override
    public String sayHello() throws RemoteException {
        return "Hello, world!";
    }
}

public class HelloProxy implements Hello {

    private HelloImpl helloImpl;

    public HelloProxy(String host, int port) throws RemoteException {
        helloImpl = (HelloImpl) java.rmi.Naming.lookup("rmi://" + host + ":" + port + "/Hello");
    }

    @Override
    public String sayHello() throws RemoteException {
        return helloImpl.sayHello();
    }
}

public class Main {

    public static void main(String[] args) throws RemoteException {
        Hello proxy = new HelloProxy("localhost", 1099);
        System.out.println(proxy.sayHello());
    }
}

In this example, the HelloImpl class implements the Hello interface. This interface defines a single method, sayHello(), which returns a string. The HelloProxy class implements the Hello interface as well. However, it does not actually implement the sayHello() method. Instead, it delegates the call to the sayHello() method of the HelloImpl object. The HelloProxy class is created by passing the host and port of the remote object to the constructor. The main() method creates a HelloProxy object and calls the sayHello() method. The sayHello() method of the HelloProxy class delegates the call to the sayHello() method of the HelloImpl object. The HelloImpl object is located on the remote host and is accessible through the RMI protocol.

Here is another example

import java.util.concurrent.Callable;

public class ExpensiveObject {

    private void initialize() {
        System.out.println("Initializing expensive object...");
    }

    public void doSomethingExpensive() {
        System.out.println("Doing something expensive...");
    }
}

public class ExpensiveObjectProxy implements Callable<ExpensiveObject> {

    private ExpensiveObject expensiveObject;

    public ExpensiveObjectProxy() {
        expensiveObject = new ExpensiveObject();
    }

    @Override
    public ExpensiveObject call() throws Exception {
        expensiveObject.initialize();
        return expensiveObject;
    }
}

public class Main {

    public static void main(String[] args) throws Exception {
        ExpensiveObjectProxy proxy = new ExpensiveObjectProxy();
        ExpensiveObject expensiveObject = proxy.call();
        expensiveObject.doSomethingExpensive();
    }
}

In this example, the ExpensiveObject class represents a heavy object. The ExpensiveObject class has two methods: initialize() and doSomethingExpensive(). The initialize() method initializes the object, and the doSomethingExpensive() method does something expensive. The ExpensiveObjectProxy class implements the Callable interface. This interface defines a single method, call(), which returns an object. The call() method of the ExpensiveObjectProxy class creates an ExpensiveObject object and initializes it. The ExpensiveObject object is returned by the call() method. The main() method creates an ExpensiveObjectProxy object and calls the call() method. The call() method creates an ExpensiveObject object and initializes it. The ExpensiveObject object is returned by the call() method. The main() method then calls the doSomethingExpensive() method of the ExpensiveObject object.

The ExpensiveObjectProxy class is a proxy object for the ExpensiveObject object. The ExpensiveObjectProxy class delegates the call to the initialize() and doSomethingExpensive() methods of the ExpensiveObject object. The ExpensiveObjectProxy class is used to avoid the overhead of initializing the ExpensiveObject object if it is not needed.

Benefits of Proxy Design Pattern

The proxy design pattern is a powerful tool that can be used to solve a variety of problems. It is a good design pattern to know about if you are working on a software project that needs to control access to resources, provide a layer of abstraction, improve performance, or handle errors.

Here are some of the benefits of using the proxy design pattern:

  • Increased performance: A proxy can improve performance by caching data or by performing tasks in advance.
  • Improved security: A proxy can improve security by controlling access to resources or by preventing unauthorized modifications.
  • Increased flexibility: A proxy can make it easier to change the implementation of an object without affecting its clients.
  • Reduced coupling: A proxy can reduce coupling between objects by providing a layer of abstraction.

Drawbacks of Proxy Design Pattern

Here are some of the drawbacks of using the proxy design pattern:

  • Increased complexity: A proxy can add complexity to a system by introducing additional objects and interfaces.
  • Increased overhead: A proxy can add overhead to a system by performing additional tasks, such as caching data or checking permissions.
  • Potential for errors: If a proxy is not implemented correctly, it can introduce errors into a system.

Conclusion

Overall, the proxy design pattern is a powerful tool that can be used to solve a variety of problems. However, it is important to weigh the benefits and drawbacks of using the proxy design pattern before deciding whether or not to use it.

Further Readings

Below are some textbooks to refer to to grasp more understanding of the Proxy Design Pattern:

  • Design Patterns: Elements of Reusable Object-Oriented Software by the Gang of Four (GoF) is a classic book that covers all of the major design patterns, including the proxy pattern.
  • Head First Design Patterns by Elisabeth Freeman and Kathy Sierra is a more approachable book that uses a fun and engaging style to teach design patterns including Proxy patterns.
  • Effective Java 3rd Edition by Joshua Bloch covers the proxy pattern in Chapter 17, “Caching”.
  • Java Design Patterns by Martin Fowler is a comprehensive book that covers all of the major design patterns, including the proxy pattern.
  • https://en.wikipedia.org/wiki/Proxy_pattern
  • Checkout our other blogs on Design Patterns including Creational Design Patterns & Structural Design Patterns

Feel free to share your thoughts on this topic in the comments section below 👇 We would be happy to hear and discuss the same 🙂

Leave a comment

Your email address will not be published. Required fields are marked *