This article is a personal study memo. I am writing an article driven by the obsession that what I input must be output. I am writing this article on Qiita with the hope that someone who is familiar with it will be able to point out mistakes and give advice.
I live a working life as an engineer, but I haven't learned about design patterns properly, so I studied.
What is described here https://github.com/ck-fm0211/notes_desigh_pattern I'm uploading to.
I studied about design patterns (personal memo) Part 1 I studied about design patterns (personal memo) Part 2 I studied about design patterns (personal memo) Part 3 I studied about design patterns (personal memo) Part 4 I studied about design patterns (personal memo) Part 5 I studied about design patterns (personal memo) Part 6 I studied about design patterns (personal memo) Part 7
--Proxy: Proxy --The proxy object is entrusted with processing that can be done by someone other than the person himself / herself. --The principal object takes over and processes the processing that cannot be done by the proxy object.
--Mr. Tanaka, who is negotiating with customers as a salesperson ――I received some questions from customers, but some of them could not be answered, so I took them home ――I checked with my boss, Mr. Suzuki, and answered the customer again. --Agent object: Mr. Tanaka --Personal object: Mr. Suzuki
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod
class Sales(metaclass=ABCMeta):
"""Sales interface"""
def __init__(self):
pass
@staticmethod
@abstractmethod
def question1():
pass
@staticmethod
@abstractmethod
def question2():
pass
@staticmethod
@abstractmethod
def question3():
pass
class Suzuki(Sales):
"""Suzuki-san class (personal object)"""
@staticmethod
def question1():
print("Answer 1")
@staticmethod
def question2():
print("Answer 2")
@staticmethod
def question3():
print("Answer 3")
class Tanaka(Sales):
"""Mr. Tanaka class (agent object)"""
@staticmethod
def question1():
print("That is "Answer 1"")
@staticmethod
def question2():
print("That is "Answer 2"")
@staticmethod
def question3():
print("that is"")
#I can't answer, so ask Mr. Suzuki
Suzuki().question3()
print("Will be")
class Client:
"""Customer class"""
@staticmethod
def main():
#Question 1
Tanaka().question1()
#Question 2
Tanaka().question2()
#Question 3
Tanaka().question3()
if __name__ == '__main__':
c = Client()
c.main()
That is "Answer 1"
That is "Answer 2"
that is"
Answer 3
Will be
--Sending a request to an object is the same as calling a method on that object. --The content of the request is expressed by what kind of argument is passed to the method. --If you want to send various requests, you have to increase the number and types of arguments. --Make the request itself an object and pass that object as an argument. --A pattern that makes a request a Command object and allows you to use multiple combinations of them.
――In the science class, I decided to conduct an experiment on saturated saline solution, "How many grams of salt dissolve in 100g of water?" The procedure is as follows.
――We will also conduct an experiment, "How many g of water is needed to dissolve all 10 g of salt?" The procedure is as follows.
――It is difficult for all the students to describe the experiment method, so I will prepare an experiment set that contains the experiment method and give it to the students to experiment.
# -*- coding:utf-8 -*-
ADD_SALT = 1 #When adding salt and stirring
ADD_WATER = 2 #When adding water and stirring
class Beaker:
"""Experimental set"""
def __init__(self, water: float, salt: float):
self._water = water
self._salt = salt
self._melted = False
self.mix()
def mix(self):
"""
Method to stir the solution
Set whether it melted or remained unmelted
The concentration of saturated saline solution at room temperature is about 26.4%
"""
if self.get_density() < 26.4:
self._melted = True
else:
self._melted = False
def is_melted(self) -> bool:
return self._melted
def add_salt(self, salt: float):
self._salt += salt
def add_water(self, water: float):
self._water += water
def get_density(self):
return (self._salt/(self._water + self._salt))*100
def note(self):
print(f"water:{self._water}g")
print(f"Salt:{self._salt}g")
print(f"concentration:{self.get_density()}%")
def experiment(self, param: int):
"""Method to perform experiment"""
if param == ADD_SALT:
#When conducting an experiment to make a saturated saline solution by adding 1 g of salt at a time
#Add salt while completely dissolved
while self.is_melted():
self.add_salt(1) #Add 1g of salt
self.mix() #mix
print("Experiment to add 1g of salt at a time")
self.note()
elif param == ADD_WATER:
#When conducting an experiment to make a saturated saline solution by adding 10 g of water at a time
#Add water while remaining undissolved
while not self.is_melted():
self.add_water(10) #Add 10g of water
self.mix() #mix
print("Experiment to add 10g of water at a time")
self.note()
class Student:
"""Student to experiment"""
def main(self):
#Experiment to make saturated salt solution by adding 1 g of salt to 100 g of water
Beaker(100, 0).experiment(ADD_SALT)
#Experiment to make saturated salt solution by adding 10 g of water to 10 g of salt
Beaker(0, 10).experiment(ADD_WATER)
if __name__ == '__main__':
s = Student()
s.main()
--If you want to do an additional experiment (an experiment to make 100g of saline solution with a concentration of 10%), you have to modify the method of conducting the experiment of the experiment set class.
MAKE_SALT_WATER = 3 #When making saline solution
# ...
class Beaker:
# ...
def experiment(self, param: int):
"""Method to perform experiment"""
if param == ADD_SALT:
# ...
elif param == ADD_WATER:
# ...
elif param == MAKE_SALT_WATER:
#Experiment to make saline solution
self.mix()
#Measure the concentration and write it in a notebook
print("Experiment to make saline solution")
self.note()
# ...
--If you increase the number of experiment patterns, you will have to add if statements to the experiment set class, and you will have to increase the parameters, which is not extensible. --Stop expressing the contents of the experiment with int, and express the experiment itself with one Command object. --By giving the experiment content, that is, the Command object, a common interface, the experiment set class can execute the method that performs the common experiment regardless of the type of experiment content (Command object) received. ..
Experimental code
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod
class Beaker:
"""Experimental set"""
def __init__(self, water: float, salt: float):
self._water = water
self._salt = salt
self._melted = False
self.mix()
def mix(self):
"""
Method to stir the solution
Set whether it melted or remained unmelted
The concentration of saturated saline solution at room temperature is about 26.4%
"""
if self.get_density() < 26.4:
self._melted = True
else:
self._melted = False
def is_melted(self) -> bool:
return self._melted
def add_salt(self, salt: float):
self._salt += salt
def add_water(self, water: float):
self._water += water
def get_density(self):
return (self._salt/(self._water + self._salt))*100
def note(self):
print(f"water:{self._water}g")
print(f"Salt:{self._salt}g")
print(f"concentration:{self.get_density()}%")
Common interface
class Command(metaclass=ABCMeta):
"""A superclass that provides a common interface for classes that represent experiment content"""
def __init__(self):
self._beaker = None
def set_beaker(self, beaker: Beaker):
self._beaker = beaker
def execute(self):
pass
Each experiment content class (Command object)
class AddSaltCommand(Command):
"""Command class for experiments in which 1 g of salt is added at a time"""
def execute(self):
while self._beaker.is_melted():
self._beaker.add_salt(1)
self._beaker.mix()
print("Experiment to add 1g of salt at a time")
self._beaker.note()
class AddWaterCommand(Command):
"""Command class for experiments in which 10 g of water is added at a time"""
def execute(self):
while not self._beaker.is_melted():
self._beaker.add_water(10)
self._beaker.mix()
print("Experiment to add 10g of water at a time")
self._beaker.note()
class MakeSaltWaterCommand(Command):
"""Command class for experiments to make saline solution"""
def execute(self):
self._beaker.mix()
print("Experiment to make saline solution")
self._beaker.note()
Student class
class Student:
"""Student to experiment"""
def main(self):
add_salt = AddSaltCommand()
add_salt.set_beaker(Beaker(100, 0)) #Prepare a beaker containing 100g of water
add_water = AddWaterCommand()
add_water.set_beaker(Beaker(10, 10)) #Prepare a beaker containing 10 g of salt
make_saltwater = MakeSaltWaterCommand()
make_saltwater.set_beaker(Beaker(90, 10)) #Prepare a beaker containing 90g of water and 10g of salt.
add_salt.execute() #Experiment to make saturated salt solution by adding 1 g of salt to 100 g of water
add_water.execute() #Experiment to make saturated salt solution by adding 10 g of water to 10 g of salt
make_saltwater.execute() # 10%Experiment to make 100g of salt solution
--By applying the Command pattern, you can add various experiments without changing the source code of the experiment set. ――It is also possible to create a new experiment by combining existing experiment contents. --If you describe the execute method of the existing experiment content in the execute method of the new experiment content, when the new experiment content is executed, the existing experiment content will also be executed in the order described. ――Reusability is also high.
--Interpreter: Interpreter / explainer --There are times when you want to perform some processing based on the analysis results of the contents of a file written in some format. ――The Interpreter pattern is the most suitable pattern to realize the processing according to the procedure obtained by such "analysis result".
――Think about how to make cup ramen --The process is as follows
--Try to extract "process" and "process target" from this syntax tree. --There are two categories of "processing": "add" and "wait for 3 minutes". --On the other hand, the items classified as "processed" are not only "powdered soup", "noodles", "hot water", and "liquid soup" but also "powdered soup plus noodles" and "powdered soup plus noodles". "Hot water added" and "powdered soup plus noodles plus hot water for 3 minutes" are also considered to be treated. --In this way, the "processing target" also includes the "processing result", so in order to identify the two, the Interpreter pattern has the same structure as the Composite pattern.
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod
class Operand(metaclass=ABCMeta):
"""Interface representing the processing target"""
@abstractmethod
def get_operand_string(self):
pass
--Class that represents "processing target" and "processing result" implements this interface
class Ingredient(Operand):
"""Class that represents the processing target"""
def __init__(self, operand_string: str):
self._operand_string = operand_string
def get_operand_string(self) -> str:
return self._operand_string
class Expression(Operand):
"""Class that represents the processing result"""
def __init__(self, operator):
"""Takes an operator that represents the processing content as an argument"""
self._operand_string = None
self._operator = operator
def get_operand_string(self):
return self._operator.execute().get_operand_string()
--The interface and implementation class that represent the process are as follows.
class Operator(metaclass=ABCMeta):
"""Interface representing processing"""
@abstractmethod
def execute(self):
pass
class Plus(Operator):
"""A class that represents the process of adding"""
def __init__(self, operand1: Operand, operand2: Operand):
self._operand1 = operand1
self._operand2 = operand2
def execute(self) -> Operand:
return Ingredient(f"{self._operand1.get_operand_string()}When{self._operand2.get_operand_string()}Add")
class Wait(Operator):
"""A class that represents the process of "waiting""""
def __init__(self, minute: int, operand: Operand):
self._minute = minute
self._operand = operand
def execute(self) -> Operand:
return Ingredient(f"{self._operand.get_operand_string()}To{self._minute}What was separated")
――When you execute it, it looks like this
if __name__ == '__main__':
#Material
material1 = Ingredient("noodles")
material2 = Ingredient("Powder soup")
material3 = Ingredient("hot water")
material4 = Ingredient("Liquid soup")
#Process
#Add noodles and powdered soup
step1 = Plus(material1, material2).execute()
#Add hot water
step2 = Plus(step1, material3).execute()
#Wait 3 minutes
step3 = Wait(3, step2).execute()
#Add liquid soup
step4 = Plus(step3, material4).execute()
print(f"{step4.get_operand_string()}: That's cup ramen!")
Noodles and powdered soup added, hot water added for 3 minutes, and liquid soup added: That's cup ramen!
--In the Interpreter pattern, one grammatical rule is expressed by one class. --In the sample case, the processes such as "add" and "wait" are expressed in one class, and it is possible to execute the processes according to the analysis result of the syntax.
――Finally it's all over ――It seems that you have to be very conscious of these in order to master them in actual business ――I found out that I needed to practice by reading it over and over and writing in different patterns. -** There is a long way to go before you can write good code ** ...
Recommended Posts