Python Design Patterns - Proxy Pattern - Controlling object access


Introduction

Proxy design patterns fall under structural design patterns category. It basically provides interface to real object which can be of different types such as networking, large in memory, file etc. Proxy class acts as intermediate to requester and provider. In serving web requests, proxy server is the best example for this pattern. When any new request comes to proxy server, it will evaluate the request and then will forward request to appropriate server and get response to give back to requester. This proxy server also provides additional security and other functionalities.

Proxy pattern can be used in following scenarios -

  • When complex system is built, it would be helpful to provide proxy interface for client's benefit.
  • When you need more security before reaching to real objects
  • When you need multiple server's access through one server with security
  • To avoid loading heavy memory real objects until they needed to be loaded.

Let's understand proxy pattern using example of Personal Assistant and CEO

Personal Assistant handles schedule and availability of CEO for others to meet. If someone outside of the company wants to meet or talk to CEO, He will not be able to talk to CEO directly instead he has to contact Personal Assistant first and check whether he is allowed to talk or meet to CEO. If yes then and only CEO will meet or talk. So here Personal Assistant acts as proxy person for CEO. Let's implement this example in python to understand better in program.

class CEO(object):
    def __init__(self):
        self.is_available = True

    def occupied(self):
        self.is_available = False
        print("CEO is busy in meeting")

    def available(self):
        self.is_available = True
        print("CEO is available for meeting")

    @propery
    def status(self):
        return self.is_available

class PersonalAssistant(object):
    def talk(self):
        self.ceo = CEO()
        if self.ceo.status:
            self.actor.occupied()
        else:
            self.actor.available()


if __name__ == '__main__':
    pa = PersonalAssistant()
    pa.talk()

In above program, CEO class is real object and those functions will be accessible through only PersonalAssistant class. If CEO is available then user will be allowed to talk to CEO else you must wait for being CEO available. abc.pdf Sometimes proxy works as cache server. For example image server, where user request for image proxy will request to real server for that and will store the identity of that and image as well and next time if request comes for the same image, proxy will not reach to real server and instead it will respond with cached image.

class RealImages(object):
    def get_image(self, id):
        print("Here is your image - Image 1")

class Proxy(object)
    def __init__(self):
        self.real_images = RealImages()
        self.images = []

    def get_image(self, id):
        if id in self.images:
            print("Here is your cached image")
        else:
            self.real_images.get_image(id)

Above program does following three things - - Surrogate class to real objects. - Provides layer for supporting distribution or real objects - Delegate and protect real objects.

Different types of proxies - 1. Virtual proxy 2. Remote proxy abc.pdf 3. Protective proxy 4. Smart proxy

Virtual Proxy

Virtual proxy acts as a placeholder for heavy objects. For example, display only placeholder images on web page and saw real heavy image only when user click on it and special requests to see. Similar to that, When client really needs object virtual proxy will initiate the object else will provide placeholder object.

Remote Proxy

Remote proxy acts as a gate to access remote objects or resources. Let's say for example, images are available on the server which is not accessible from outside world except one server which acts as proxy server. In this case, when user requests for any image, proxy server will request to secure server and will return the image to user so, this kind of proxy is called remote proxy.

Protective proxy

Protective proxy controls access of the real object. For example, Many services are required to provide functionality and in this case without authorization no one can access those services so, we create intermediate service which stop unrecognized requests accessing core services. This proxy is called protective proxy.

Smart Proxy

Smart proxies helps to control access of object by multiple services. For example, In application many services are there to provide functionalities. When client tries to call those services to accomplish his work and in this process services are using shared resources and they might collide while editing that so, to solve abc.pdfthat problem proxy class is implemented to handle that access of the object according whether object is in use or not. That's how smart proxy works.

Now let's look at the real life example -

import abc

class AbstractCmd(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def execute(self, command):
        pass

class RealCmd(AbstractCmd):

    def execute(self, command):
        print(f"{command} command executed.")


class ProxyCmd(AbstractCmd):

    def __init__(self, user):
        self.is_authorized = False
        if user == "admin":
            self.is_authorized = True
        self.executor = RealCmd()
        self.restricted_commands = ['rm', 'mv']

    def execute(self, command):
        if self.is_authorized:
            self.executer.execute(command)
        else:
            if any([command.strip().startswith(cmd)
                    for cmd in self.restricted_commands]):
                raise Exception(f"{command} command is not allowed for non-admin users.")
            else:
                self.executer.execute(command)


if __name__ == '__main__':
    admin_executor = ProxyCmd("Admin")
    other_executor = ProxyCmd("Other")
    try:
        admin_executor.execute("ls -la");
        admin_executor.execute("rm -rf /");
        other_executor.execute("ls -la");
        other_executor.execute("rm -rf");
    except Exception as e:
        print(e)

In above example, ProxyCmdExecutor class helps checking security before actually executing command. So only if ProxyCmdExecutor thinks, user can execute the command then and only RealCmdExecutor will be called to execute execute method of the RealCmdExecutor class.

Proxy class and RealObject class both are implementing same method. Though Proxy class method has additional functionality which helps not calling real object methods unnecessarily.

Benefits of Proxy pattern

  • It helps to improve performance by caching heavy objects and frequently used objects.
  • It helps to implement authorization to restrict real objects access.
  • It also helps in networking application like monitoring distributed systems.

Thanks for reading this article. I hope you have gained something from this blog.

blog comments powered by Disqus