Learn the design pattern "Proxy" in Python

As a material for learning GoF design patterns, the book "Introduction to Design Patterns Learned in the Augmented and Revised Java Language" seems to be helpful. However, since the examples taken up are based on JAVA, I tried the same practice in Python to deepen my understanding.

■ Proxy (proxy pattern)

The Proxy pattern is a type of design pattern in programming. A Proxy is, broadly speaking, a class that acts as an interface to something else. The "other thing" can be anything, such as a network connection, a large object in memory, or some resource that is costly or impossible to duplicate. A well-known example of the Proxy pattern is a pointer object with reference counting. In situations where multiple copies of complex objects are required, adding a Flyweight pattern to the Proxy pattern can reduce memory usage. Normally, you create only one instance of a complex object and multiple proxy objects. Those proxy objects contain only references to complex objects. Operations on the proxy are forwarded to the original object. When all proxy objects are destroyed, the memory used by the referenced complex object is also freed.

UML class and sequence diagram W3sDesign_Proxy_Design_Pattern_UML.jpg UML class diagram proxy.png (The above is quoted from Wikipedia)

■ "Proxy" sample program

I would like to actually run a sample program that utilizes the Proxy pattern and check the following behavior.

--First, accept "Alice" as a printer agent --Prepare the printer so that it can be used (time required is about 10 seconds) --Actually, use the printer to perform the following printout process "** Nice to meet you ". --Next, accept "Bob" as a printer agent --Actually, use the printer to perform the following printout process " Hello, World **".

(1) Treat the current printer agent as a printer user (2) Display the name of the printer user by enclosing it in "===" before and after. (3) Display the character string.
$ python Main.py 
The name of the Printer agent is now(Alice)is
Printer instance(Alice)Is being created..........Done
===Printer user(Alice) ===
Nice to meet you

The name of the Printer agent is now(Bob)is
===Printer user(Bob) ===
Hello, World

The confirmation point here is that the Printer instance (xxx) is being created .......... Done "</ font> is displayed only once. It is a point. In the actual sample program, Singleton pattern is used so that an instance of a complex object can be created only once.

■ Details of sample program

Similar code has been uploaded to the Git repository. https://github.com/ttsubo/study_of_design_pattern/tree/master/Proxy

--Directory structure

.
├── Main.py
└── proxy
    ├── __init__.py
    └── printer_proxy.py

(1) The role of Subject

Defines an interface for identifying the Proxy role and the RealSubject role. Thanks to the Subject role, the Client role does not need to be aware of the difference between the Proxy and RealProxy roles. In the sample program, the Printable class serves this role.

proxy/printer_proxy.py


class Printable(metaclass=ABCMeta):
    @abstractmethod
    def setPrinterName(self, name):
        pass

    @abstractmethod
    def getPrinterName(self):
        pass

    @abstractmethod
    def myPrint(self, string):
        pass

(2) Role of Proxy

The Proxy role handles requests from the Client role as much as possible. If you can't handle it by yourself, the Proxy role will leave the job to the RealSubject role. The Proxy role will generate the RealSubject role after you really need the RealSubject role. The Proxy role implements the interface defined by the Subject role. In the sample program, the PrinterProxy class serves this role.

proxy/printer_proxy.py


class PrinterProxy(Printable):
    def __init__(self, name):
        self.__name = name
        self.__real = None

    def setPrinterName(self, name):
        self.__name = name

    def getPrinterName(self):
        return self.__name

    def myPrint(self, string):
        self.__real = Printer.getPrinter(self.__name)
        self.__real.myPrint(string)

(3) The role of Real Subject

The role of "Real Subject" of "Person" appears when the role of Proxyof" Proxy "is out of control. Like theProxy role, this role also implements the interface defined by the Subjectrole. In the sample program, thePrinter` class serves this role.

proxy/printer_proxy.py


class Printer(Printable):
    @classmethod
    def getPrinter(cls, name):
        if not hasattr(cls, "_instance"):
            cls._instance = cls(name)
        else:
            cls._instance.__name = name
        return cls._instance

    def __init__(self, name):
        self.__name = name
        self.__heavyJob('Printer instance({})Is being created'.format(self.__name))

    def setPrinterName(self, name):
        self.__name = name

    def getPrinterName(self):
        return self.__name

    def myPrint(self, string):
        print('===Printer user({}) ==='.format(self.__name))
        print(string)
        print("")

    def __heavyJob(self, msg):
        print(msg, end='')
        for _ in range(10):
            time.sleep(1)
            print('.', end='')
        print('Done')

(4) The role of Client

It is a role that uses the Proxy role. In the sample program, the startMain method serves this role.

Main.py


from proxy.printer_proxy import PrinterProxy

def startMain():
    p = PrinterProxy("Alice")
    print("The name of the Printer agent is now({})is".format(p.getPrinterName()))
    p.myPrint("Nice to meet you")
    p.setPrinterName("Bob")
    print("The name of the Printer agent is now({})is".format(p.getPrinterName()))
    p.myPrint("Hello, World")

if __name__ == '__main__':
    startMain()

■ Reference URL

-[Finishing "Introduction to Design Patterns Learned in Java Language" (Not)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java Introduction to Design Patterns Learned in Languages-Finishing-No-2cc9b34a30b2) -[Qiita article "Design Pattern #Proxy" (https://qiita.com/nirperm/items/cb200058edb935adb26c)

Recommended Posts