Introduction
The proxy-pattern is a very useful design pattern. The basic function is to make sure that a caller has no direct access to the instance of an object but rather goes through another class which does have access.
I know this all sounds rather cryptic, so maybe a diagram will help here:

This could be the diagram for some form of a very simplified mobility service.
So, what do we see here?
- A Drivable interface with exactly one method: drive(int instance)
- Two classes, Car and Bicycle which implement the Drivable interface
- A VehicleProxy class which implements the Drivable interface, but which also holds a concrete class, in this case a Car class, which also implements the Drivable interface.
- A concrete VehicleManager class which holds an instance of the VehicleProxy. The class has no knowledge of the extact kind of vehicle of will be driven, nor of any of the implementation details.
This pattern can be very useful in some cases:
- If you want to control access to the concrete class
- If you want to have conditional access to the concrete classes. In this particular example, the VehicleProxy class could also be given the age of the driver as a parameter. If the driver is younger that 18, he or she is not allowed to drive a care, for example.
Implementation in Python
Implementing this pattern in Python turns out to be quite easy.
We start by importing this:
from abc import ABC, abstractmethod
Why do we need these?
- The
ABCstands for ‘Abstract Base Class’, which means a class will contain some abstract method, that is methods without an implementation which there need to implemented in a subclass. - These methods are indicate by using the
abstractmethodindicator.
The next thing we need to do is define a common interface. Since Python has no concept of interfaces, traits or protocols, we use a class deriving from ABC with one abstract method:
class Drivable(ABC):
@abstractmethod
def drive(self, distance: int) -> None:
pass
Next we need to implement this class. We do this by subclassing it into two subclasses, Car and Bike:
class Car(Drivable):
def drive(self, distance: int) -> None:
print(f"Driving a car for {distance} kilometers.")
class Bike(Drivable):
def drive(self, distance: int) -> None:
print(f"Riding a bike for {distance} kilometers.")
Note that both classes implement the drive method, also notice the type-annotation.
Now we implement the SecureVehicleProxy class:
class SecureVehicleProxy(Drivable):
def __init__(self, vehicle: Drivable, user_has_license: bool = True):
self._vehicle : Drivable = vehicle
self._user_has_license: bool = user_has_license
self._total_distance: int = 0
def drive(self, distance: int) -> None:
if not self._user_has_license:
print("Access denied: No valid license!")
return
print(f"[LOG] Starting vehicle operation...")
self._vehicle.drive(distance)
self._total_distance += distance
print(f"[LOG] Total distance driven: {self._total_distance} km")
Line by line:
- The class has three fields: a _vehicle property of type
Drivablewhich represents the vehicle that will be driven. - For some vehicles you need license, for example a driver’s license, which is represented by the
_user_has_licensefield. - We also want to keep track of the distance driven by this vehicle, this is done using the
_total_distancefield. - In the
drive()method we do couple things:- We check if the user has a valid license. If not, print an error message and return.
- Call the
drive()method on the actual vehicle. - Update the total distance driven.
Time to test
Now we can test our pattern:
if __name__ == "__main__":
car = Car()
licensed_proxy = SecureVehicleProxy(car, user_has_license=True)
licensed_proxy.drive(100)
print("\n" + "="*40 + "\n")
unlicensed_proxy = SecureVehicleProxy(car, user_has_license=False)
unlicensed_proxy.drive(50)
Line by line:
- We start by constructing a
Carobject. - Next we construct a
SecureVehicleProxyobject, passing the car as one of the parameters to the constructor. We also make sure that user has a license. - Using the proxy we can now drive 100 kilometers.
- Print a separator.
- Finally we create a proxy for an unlicensed user, and see the result of trying to drive a vehicle.
Conclusion
The proxy pattern provides a powerful and flexible way to control access to a core object. As demonstrated through the Drivable example, the SecureVehicleProxy class acts as an intermediary, adding a crucial layer of security and management. By implementing the same interface as the real object, the proxy can seamlessly intercept method calls, allowing you to add functionality like access control, logging, or performance monitoring without modifying the original class.
This separation of concerns is a cornerstone of good software design, making your code more maintainable and scalable. Instead of cluttering your Car or Bike classes with security checks, the proxy handles this responsibility, keeping your core business logic clean. This pattern is particularly useful in scenarios where you need to manage complex interactions, like controlling network requests, handling lazy loading of resources, or, as shown, implementing robust access control. Ultimately, understanding and applying the proxy pattern empowers you to build more robust and secure applications by strategically managing object interactions.




