Proxy Design Pattern in Python
What is it?
The Proxy Design Pattern is adopted when you need to postpone the instantiation of a resource-intensive object. A resource-intensive object is an object which requires significant amount of system resources, and hence is expensive to create such as a large file on disk, a network connection etc. The Proxy acts as a placeholder for the actual object. The latter is created by the Proxy only when it is absolutely necessary to create it, or when certain preconditions are met before methods of the real object are invoked. In our example, the resource-intensive object is the Car object, and we will allow the user to drive it only if he is of age.
For the client, accessing the Proxy is similar to accessing the actual object because both have similar methods. The client interacts with the Proxy object most of the time until the object creation becomes mandatory.
The Proxy can also be used to add extra functionality (such as Caching operations) to the actual object. In this way, it is related to the Decorator Pattern.
It is classified under Structural Design Patterns as it offers an industry-wide popular way to organize class hierarchy.
Why the need for it: Problem Statement
The problem that the Proxy Pattern is trying to solve is to postpone the creation of a resource-intensive object unless its creation is necessary or certain preconditions are met.
Terminology
- Proxy: The Proxy is a placeholder class, which creates the resource-intensive object only if it is necessary. The Proxy has similar methods as those of the resource-intensive object. These methods invoke corresponding methods of the resource-intensive object only if it certain conditions are met. This leads to instantiation of the actual object AND calls to its methods, only when they are required, to prevent unnecessary overhead.
Pseudo Code
Consider the following situation: We create an instance of the class Car only when the driver is of permissible age. Our Proxy is in this case is another class CarProxy, who is checking to see if the age of the driver is 18 or above. If yes, then the car is created and the driver is allowed to drive it. If not, the car is not created and consequently driver cannot drive it.
class Car: def driveCar(): print 'Driving Car...' class CarProxy: def __init__(): ageOfDriver = 15 car = None def driveCar(): if ageOfDriver >= 18: create instance of Car object & assign to local variable call driveCar() of the Car object else: print 'Driver is underage. Can't drive the car.' # USING THE SETUP ABOVE create an instance of the proxy call the driveCar() of the proxy; driver shouldn't be allowed since default age is 15 in the proxy class alter ageOfDriver to 21 call the driveCar() of the proxy, which instantiates the car object and calls its driveCar()
How to implement it
class Car: '''Resource-intensive object''' def driveCar(self): print("Driving Car....") class CarProxy: '''Relatively less resource-intensive proxy acting as middleman. Instantiates a Car object only if the driver is of age.''' def __init__(self): self.ageOfDriver = 15 self.car = None def driveCar(self): print("Proxy in action. Checking to see if the driver is of age or underage...") if self.ageOfDriver >= 18: # If driver is of age, let him drive the car. self.car = Car() self.car.driveCar() else: # Otherwise, don't instantiate the car object. print("Driver is underage. Can't drive the car.") # Instantiate the Proxy carProxy = CarProxy() # Client attempting to drive a car at the default age of 15. Logically, since he/she cannot have a driving license, there is no need to buy a car, or, in our case, make the car object. carProxy.driveCar() # OUTPUT: # Proxy in action. Checking to see if the driver is of age or underage... # Driver is underage. Can't drive the car. # Altering the age of the driver carProxy.ageOfDriver = 21 # Client attempting to drive a car at the default age of 21. Should succeed. carProxy.driveCar() # OUTPUT: # Proxy in action. Checking to see if the driver is of age or underage... # Driving Car....
Walkthrough of implementation
- First, we create an object of the Proxy (CarProxy) i.e. carProxy. This carProxy has two attributes, ageOfDriver set to 15 & an empty car variable.
- Then, we call its driveCar() method just like we would have if we had created a Car object.
- The driveCar() of the Proxy checks if ageOfDriver is greater than or equal to 18. It isn't, so it prints a message saying that driver is underage.
- Then, we modify the ageOfDriver attribute of the Proxy object, setting it to 21.
- We call the driveCar() of the Proxy again, and this time, it detects that driver is of age. So, it instantiates a Car object and calls its driveCar() method.
Related to: Adapter & Decorator
- Creational Patterns
- Factory
- Abstract Factory
- Prototype
- Singleton
- Builder
- Architectural Pattern
- Model View Controller (MVC)