Open In App

Proxy Design Pattern for Object Communication in Python

Improve
Improve
Like Article
Like
Save
Share
Report

Memory and time are the two main challenges while working with large objects. So, it is important to reuse these objects to generate new references rather than creating them on every new request. And most importantly, memory must be released after all sections of code have completed working on it.

A proxy design pattern is the best possible way to achieve the initialization of large objects. It separates the client code from the object by creating a surrogate proxy object that behaves like a real object. Here, the client code interacts with the proxy class – it has a real-object instance. Apart from object initialization, it also provides the best possible solutions for logging, network connections, access to shared objects, reference counting, and more.

Some advantages of using a proxy design pattern are:

  1. It ensures distributed, controlled, or intelligent access by using an extra level of indirection
  2. It protects the real object from unnecessary complexity by adding a wrapper.
  3. Avoids inapt object instantiation and can optimize the performance of an application

However, sometimes, the proxy pattern increases the response time from the object – when the object is requested for the first time, it will take more time for object initialization.

Implementation

Let’s implement a Python code to instantiate a large object, which holds 10 million digits – RealClass. Since it holds a large object, it is better to interact with the object using a proxy  â€“ ProxyClass  â€“ rather than initiating direct communication.

Proxy Design Pattern

First and foremost, let’s create an abstract class  â€“ AbstractClass  â€“ that provides an interface for RealClass and ProxyClass. The abstract class has a method called sort_digits, and the real class inherits from the abstract class. The proxy class uses the instance of RealClass and facilitates client communication.

Python3




import abc
import random
  
  
class AbstractClass(metaclass=abc.ABCMeta):
    """ interface for real and proxy object """
    @abc.abstractmethod
    def sort_digits(self, reverse=False):
        pass
  
  
class RealClass(AbstractClass):
    """ RealClass that holds a larger object """
  
    def __init__(self):
        self.digits = []
  
        for i in range(1000000):
            self.digits.append(random.random())
  
    def sort_digits(self, reverse=False):
        self.digits.sort()
  
        if reverse:
            self.digits.reverse()
  
  
class ProxyClass(AbstractClass):
    """ A proxy class that has the same interface as RealClass. """
  
    ref_count = 0
  
    def __init__(self):
        """ Creates an object if it doesn't exist and caches it otherwise """
  
        if not getattr(self.__class__, 'cached_object', None):
            self.__class__.cached_object = RealClass()
            print('New object generated')
        else:
            print('Using cached object')
  
        self.__class__.ref_count += 1
        print('Reference Count:', self.__class__.ref_count)
  
    def sort_digits(self, reverse=False):
        print('Sort method')
        print(locals().items())
          
        # invokes the sort_digits method of real class
        self.__class__.cached_object.sort_digits(reverse=reverse)
  
    def __del__(self):
        """ Delete the object when the number of reference is 0 """
        self.__class__.ref_count -= 1
  
        if self.__class__.ref_count == 0:
            print('Deleting cached object')
            del self.__class__.cached_object
  
        print('Reference Count:', self.__class__.ref_count)
  
  
if __name__ == '__main__':
    proxA = ProxyClass()
    print()
  
    proxB = ProxyClass()
    print()
  
    proxC = ProxyClass()
    print()
  
    proxA.sort_digits(reverse=True)
    print()
  
    print('Deleting proxA')
    del proxA
  
    print('Deleting proxB')
    del proxB
  
    print('Deleting proxC')
    del proxC


Output:

New object generated
Reference Count: 1

Using cached object
Reference Count: 2

Using cached object
Reference Count: 3

Sort method
dict_items([(‘reverse’, True), (‘self’, <__main__.ProxyClass object at 0x7ff50f73e0b8>)])

Deleting proxA
Reference Count: 2
Deleting proxB
Reference Count: 1
Deleting proxC
Deleting cached object
Reference Count: 0

Let’s look into the ProxyClass design. It creates an instance of the RealClass if it’s not been created before. If the object already exists, the proxy class increments the reference count and returns a new link to the real class. And the sort method in the proxy class calls the sort method of the real class, using the cached reference. In the end, the destructor method decreases the reference count on every call, and when there are no references left, it deletes the object.  

Proxy design pattern optimizes the performance of an application by caching the frequently used objects and also improves the security of an application by checking the access rights. Apart from these, it facilitates remote system interactions.



Last Updated : 25 Oct, 2020
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads