Proxy Pattern

Issue

  • You want to hide certain access points of a class to Clients, in other words, control the access of a particular class or system

Solution

There are three types of proxies:
  • Remote proxy
    • Used when accessing a remote system or project

    • The proxy is then used to access this remote interface

  • Virtual proxy
    • Controls access to a resource that is expensive to create (in terms of CPU time)

    • The proxy then controls whether it is actually needed to interact with the resource it controls the access for

  • Protection proxy
    • Defines whether the caller its method is actually allowed to interact with the resource it controls (access management)

    • Controls access to resources based on access rights

  • A proxy adds additional behavior to the resource it controls, but in terms of access control

Definition

The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.

Example

Illustration of a Virtual Proxy.

Issue

  • instantiating a BookParser will parse the entire book string (which contains the entire content of a book) and does some computations while doing it (like count verbs, words, chapters, pages, …) -> it is expensive

  • calling any of its methods (like number_of_pages(), which returns the amount of pages) is very cheap, because no further computation is required to get it

  • the computation of these attributes during parsing is, on the other hand, only necessary, if any of the attribute-returning methods is actually called (which might not be the case for each instantiated BookParser)

str book = "....<entire book>..."
book_parser = BookParser(book) # expensive instantiation
print(book_parser.number_of_pages()) # cheap call

Solution

  • in order to increase performance, we only want to parse a string in the BookParser when any of its method (which require that computation) is called (make it lazy)

  • we achieve this by not directly interacting with the BookParser but with a BookParserProxy, which interacts with the BookParser

Subject:

An interface class, which is implemented by both RealSubject and Proxy to ensure that both have the same interface

RealSubject:

The class that is the actual class we want to call

Proxy:

The class which is called by a Client instead of RealSubject. Since both implement the same interface, the Client can treat the Proxy equally to RealSubject.

        classDiagram
    Subject<|--RealSubject : implements
    Subject<|--Proxy : implements
    RealSubject<--Proxy : HAS-A
    class Subject {
        <<interface>>
        +request()
    }
    class RealSubject {
        +request()
    }
    class Proxy {
        +request()
    }
    
  • the Proxy controls the access to RealSubject

  • the Proxy follows the same interface as the thing it is proxying

  • the Proxy does not have to instantiate RealSubject during its own creation, but may instantiate it in its method

Code Example

Find a template example for Python at https://refactoring.guru/design-patterns/proxy/python/example

from abc import ABC


# Subject
class BookParser(ABC):

    def get_number_of_pages(self):
        pass


# RealSubject
class RealBookParser(BookParser):

    def __init__(self, book: str):
        # some expensive computation
        print("Puuh, I'm finally done with book ...")

    def get_number_of_pages(self):
        print("That's quickly done ...")
        # returning pages


# Proxy
class LazyBookParserProxy:

    def __init__(self, book: str):
        self.book = book
        self.book_parser = None

    def get_number_of_pages(self):
        if not self.book_parser:
            self.book_parser = RealBookParser(self.book)
        return self.book_parser.get_number_of_pages()