Proxy Pattern in Java and Python
This article exists on YouTube
The entire content of this article is also available on YouTube. It's your choice: if you prefer video content, you can watch it on YouTube. If you prefer reading, feel free to continue here.
What is a Proxy Pattern?
The Proxy Pattern is a programming pattern used in software engineering and is part of the group of structural patterns. It may be used in any object-oriented programming language, such as Java, Python, C++, and so on. What it does is to provide a surrogate that acts as a substitute for another object. This means, that the surrogate object controls the access to the real object.
In other words, the surrogate functionality of a proxy allows it to act as as stand-in for another object, providing additional control or functionality as needed. This flexibility makes the Proxy Pattern a powerful tool for managing object access and behavior in various scenarios.
So what is the Proxy Pattern used for? This pattern is used in various software systems and frameworks. Examples of real-world use include the Protection Proxy, Lazy Initialization, Remote Proxy, Logging and Monitoring, such as Virtual Proxy.
A protection proxy controls access to sensitive resources or operations by enforcing access control rules. For example, a protection proxy might restrict access to certain methods or data based on user credentials or other criteria.
The proxy can delay the creation or initialization of the real object until it is actually needed. This can be useful for improving performance by avoiding the unnecessary creation of expensive resources until they are required.
In distributed systems, a proxy can act as a surrogate for an object located on a remote server. The proxy handles communication with the remote object, hiding the complexity of network communication from the client. In more simple terms there is the real object remotely and a proxy object for that locally. You will use the local object as if it was the remote object. All your calls to it will be local. Behind the scenes the calls you make will be transmitted over the network to the real object. That part though should be largely hidden to you. In Java there is RMI (Remote Method Invocation) that handles the transportation process in the background. For that to work properly method arguments and return values need to be built using either primitive variable types or serializable object types. Why? Well, the information within the method arguments and return values need to be transformed to a text version, then transmitted to the remote host, and then transformed back into method arguments and return values.
Proxies can intercept method calls to the real object and perform additional tasks such as logging method invocations, measuring performance, or implementing caching strategies.
A virtual proxy is a type of proxy that controls access to the real object by providing a lightweight placeholder. For example, a virtual proxy for a large image or document might only load the full content when it is requested by the client.
What is the structure of a Proxy Pattern?
-
Abstract Subject
Base interface or abstract class defining methods to be implemented. -
Real Subject
Implementation of the abstract subject. This is the object to which additional functionality can be added. -
Proxy Subject
Half implementation of the abstract subject. Will conditionally root through to the real subject.
Example Codes
There are a number of programming languages where the Proxy Pattern can prove useful. Every language that supports the construct of classes can make use of this pattern.
All the codes within this article can also be found on the GitHub repository programming-patterns and in there within the directory Proxy Pattern.
Java Example
The following example is a Java application putting the Proxy Pattern into action. All the following files need to be placed into the same folder so that the code within the files can work properly, because the files also reference each other. Being the smallest possible code example, this little project consists of four Java files:
- AbstractSubject.java
- RealSubject.java
- ProxySubject.java
- Main.java
The first thing that needs to be defined is the abstract subject. This is the definition of what methods the class has.
public interface AbstractSubject {
void doSomething();
}
Next we need to create the real subject. This is an implementation of the abstract subject and represents the center part of the Proxy Pattern.
public class RealSubject implements AbstractSubject {
public RealSubject() {
System.out.println("Create real instance.");
}
@Override
public void doSomething() {
System.out.println("Execute real doSomething method.");
}
}
We also need a proxy subject that will conditionally root functionality of the real subject. It also implements the abstract subject.
public class ProxySubject implements AbstractSubject {
private RealSubject realSubject;
@Override
public void doSomething() {
if (this.realSubject == null) {
this.realSubject = new RealSubject();
}
this.realSubject.doSomething();
}
}
And that is it already. The main construct of the Proxy Pattern consists of these four Java files. The only thing that remains is to apply their logic within the executing main class. The main(...) method creates an instance of the ProxySubject class. Right after that it runs the doSomething() method that in turn will also instatiate the real subject from within the proxy subject. After that it executes whatever is defined within the doSomething() method of the real subject. After that doSomething() method is called again from the main(...) method. This time though there is no instantiation, because the instance already exists. Hence, the doSomething() method of the real subject is executed right away. This leads to a series of command line interface prints.
public class Main {
public static void main(String[] args) {
System.out.println("Proxy Pattern in Java");
System.out.println("---------------------");
System.out.println();
System.out.println("Create proxy instance.");
AbstractSubject subject = new ProxySubject();
System.out.println("Call proxy doSomething method.");
subject.doSomething();
System.out.println("Call proxy doSomething method again.");
subject.doSomething();
}
}
There also needs to be a Manifest.txt file within the same folder. This is a metadata file used primarily in JAR files. It contains information about the JAR file itself and its contents. It is placed within thie META-INF folder within the JAR archive file structure.
Main-Class: Main
To compile this code into an executable Java application which would be a JAR file, a couple of commands need to be executed from within the same directory. The commands are found within the build file.
@echo off
javac *.java
jar cvfm ProxyDemo.jar Manifest.txt *.class
#!/bin/bash
javac *.java
jar cvfm ProxyDemo.jar Manifest.txt *.class
Now that the compilation has taken place, there are new files within the same directory. Every JAVA file has its equivalent CLASS file, and there is also a file called ProxyDemo.jar. This is the Java application that needs to be executed. The command for that is within the run file.
@echo off
java -jar ProxyDemo.jar
#!/bin/bash
java -jar ProxyDemo.jar
Testing the Java Example
Now that all the files are ready and within their correct location they can be put to test. First we need to compile the Java code and then we can execute it to see what it will print in the Windows Command Prompt or in the MacOS / Linux Terminal.
Python Example
The following example is a Python application putting the Proxy Pattern into action. All the following files need to be placed into the same folder so that the code within the files can work properly, because the files also reference each other. As this is the smallest possible example, this little project consists of four Python files:
- abstract_subject.py
- real_subject.py
- proxy_subject.py
- main.py
The first thing that needs to be defined is the abstract subject. This is the definition of what methods the class has.
from abc import ABC, abstractmethod
class AbstractSubject(ABC):
@abstractmethod
def doSomething(self):
pass
Next we need to create the real subject. This is an implementation of the abstract subject and represents the center part of the Proxy Pattern.
from abstract_subject import AbstractSubject
class RealSubject(AbstractSubject):
def __init__(self):
print("Create real instance.")
def doSomething(self):
print("Execute real doSomething method.")
We also need a proxy subject that will conditionally root functionality of the real subject. It also implements the abstract subject.
from abstract_subject import AbstractSubject
from real_subject import RealSubject
class ProxySubject(AbstractSubject):
def __init__(self):
self.real_subject = None
def doSomething(self):
if self.real_subject is None:
self.real_subject = RealSubject()
self.real_subject.doSomething()
And that is it already. The main construct of the Proxy Pattern consists of these four Python files. The only thing that remains is to apply their logic within the executing main class. The main() method creates an instance of the ProxySubject class. Right after that it runs the doSomething() method that in turn will also instatiate the real subject from within the proxy subject. After that it executes whatever is defined within the doSomething() method of the real subject. After that doSomething() method is called again from the main() method. This time though there is no instantiation, because the instance already exists. Hence, the doSomething() method of the real subject is executed right away. This leads to a series of command line interface prints.
from proxy_subject import ProxySubject
def main():
print("Proxy Pattern in Python")
print("-----------------------")
print()
print("Create proxy instance.")
subject = ProxySubject()
print("Call proxy doSomething method.")
subject.doSomething()
print("Call proxy doSomething method again.")
subject.doSomething()
if __name__ == "__main__":
main()
There is no need to compile anything in Python. The code is executed directly using the python command under Windows and the python3 command under MacOS / Linux.
Testing the Python Example
Now that all the files are ready and within their correct location they can be put to test. Python does not need to be compiled before being run. Therefore we can execute it right away to see what it will print in the Windows Command Prompt or in the MacOS / Linux Terminal.
More Information
So, where do I have my information from? In general, there are lots of places on the internet that offer great information. But that is just one source of information. Another source would be books. Those have the advantage that usually several people work on the same book before it is released, raising the chance for distribution of good and proven information.
Head First - Design Patterns
Building Extensible and Maintainable Object-Oriented Software
A book that has proven to be a valuable asset within my personal library is Head First - Design Patterns by the publisher O'Reilly. It has a fun approach to this topic, having recurring characters, such as the Guru, the Developer, the Skeptical Developer, Joe, and many more.
Java Design Patterns
A Hands-On Experience with Real-World Examples
Another highly valuable book is Java Design Patterns: A Hands-On Experience with Real-World Examples by the publisher Apress. It offers a conservative and rather scientific approach to the subject. It also offers code examples in Java, although any object-oriented programming language applies to the design patterns shown here.