I practiced design patterns so that I could write code that was conscious of design. Other Design Patterns will be released frequently.
The primary goal is to understand when, what, and how to use design patterns. (I'm new to Java or a statically typed language, and I don't have a long history of python, so I think there are some things that aren't like Pythonista. If you have any suggestions, please teach me.)
This time, the pattern Proxy related to the structure.
Prepare a proxy to control instance creation and access restrictions, and have the proxy do the work until it is needed. However, when the agent exceeds the range that can be done, the agent gives the role to the original person.
This sample program is a "named printer". The Main class creates a class instance (agent) for PrinterProxy. Name the instance "Alice" and display the name. Then rename it to "Bob" and display that name.
By setting or getting the name, an instance (person) of the real Printer class has not been created yet. The PrinterProxy class takes care of setting and getting the name. The PrinterProxy class instantiates the Printer class only when the my_print method is finally called and the actual printing is performed.
An interface called Printable is defined to equate the PrinterProxy class with the Printer class. Here, the sample program is created on the assumption that it takes a long time to instantiate the Printer class. In order to express that it takes time, I call a method called heavy_job from the constructor and earn a few seconds as "heavy processing".
printer.py
import sys
import time
from printable import Printable
class Printer(Printable):
def __init__(self, name):
self.__name = name
self.__heavy_job('Printer instance({0})Is being generated'.format(self.__name))
def set_printer_name(self, name):
self.__name = name
def get_printer_name(self):
return self.__name
def my_print(self, string):
print('===' + ' ' + self.__name + ' ' + '===')
print(string)
def __heavy_job(self, msg):
sys.stdout.write(msg)
for i in range(1, 5):
try:
time.sleep(1)
except InterruptedError:
pass
sys.stdout.write('.')
print('Done.')
The Printer class is a class that represents "the person". I am doing heavy_job as a "heavy job".
After that, there are set_printer_name for setting the name, get_printer_name for getting the name, and my_print for displaying the character string. The center of the Proxy pattern is towards the PrinterProxy class.
printable.py
from abc import abstractmethod
class Printable():
@abstractmethod
def set_printer_name(self, name):
pass
@abstractmethod
def get_printer_name(self):
pass
@abstractmethod
def my_printer(self, string):
pass
The Printeable interface is for equating the PrinterProxy class with the Printer class.
printer_proxy.py
from printable import Printable
from printer import Printer
class PrinterProxy(Printable):
def __init__(self, name):
self.__name = name
self.__real = None
def set_printer_name(self, name):
if (self.__real is not None):
self.__real.set_printer_name(name)
self.__name = name
def get_printer_name(self):
return self.__name
def my_print(self, string):
self.__realize()
self.__real.my_print(string)
def __realize(self):
if (self.__real is None):
self.__real = Printer(self.__name)
The PrinterProxy class acts as an agent. Implements the Printable interface. The name field is for holding the name, and the real field is for holding the "person".
In the constructor, set the name. The set_printer_name method sets a new name. If real is not None, set the name for the person as well. However, if real is None, it returns a value in the PrinterProxy name field.
Since the my_print method is a process outside the scope of this agent, call the realize method to generate the "person". After executing the realize method, the real field holds the person, so call real.print. This is "delegation".
** No matter how many times you call set_printer_name or get_printer_name, the Printer will not be instantiated. No instance of Printer is created. ** A Printer instance is created only when the "person" is needed. (PrinterProxy users do not know at all whether the person has been generated, and there is no need to worry about it.)
The realize method creates an instance of Printer if the real field is None. If the real field is not None, do nothing.
main.py
from printer_proxy import PrinterProxy
def main():
pp = PrinterProxy('Alice')
print('The name is now' + pp.get_printer_name() + 'is.')
pp.set_printer_name('Bob')
print('The name is now' + pp.get_printer_name() + 'is.')
pp.my_print('Hello, world.')
if __name__ == '__main__':
main()
Execution result
The name is now Alice.
The name is now Bob.
Printer instance(Bob)Is being generated....Done.
=== Bob ===
Hello, world.
In the Proxy pattern, the Proxy role acts as an agent and takes over the processing as much as possible. In the sample program, by using the Proxy role, heavy processing (instantiation) could be delayed until the actual my_print.
If there are many functions that take a long time to initialize in the actual usage scene, I think that they will be initialized only when it is time to actually use those functions. I think it is one of the most commonly used design patterns.
Recommended Posts