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
――The purpose is to observe changes in the state, but rather the emphasis is on "notification" rather than "observation". --When the state of an instance changes, the instance itself "notifies" the change of state to the "observer". --Example --Think about an airplane boarding pass. --If the person who purchased the boarding pass of the plane is forced to cancel, contact the airline and tell them to cancel. ――It is the "airline" that manages each passenger, and the "passenger" that notifies the cancellation. --The "passenger" will contact the "airline" if he wants or no longer needs the ticket. --With such a mechanism, airlines do not have to constantly observe all users.
--Boss and subordinates --Request multiple subordinates to create deliverables --Tell your boss the completion report
--The place to prepare the Observer interface is the key --In the Observer interface, define an update method that serves as a contact point for receiving notifications from the observation target when the observation target changes. --Create a Subject abstract class to be observed --Implement an add_observer method to freely add Observer instances and a notify_observers method to notify all of the Observer instances it holds.
--Memento: Souvenirs, keepsakes --By saving the state of the instance as a snapshot, it is possible to restore the state of the instance at that time. --The state of the instance may change steadily during program execution. --There may be requests such as "I want to return to the previous state" or "I want to return to the state at a certain point" for an instance that has changed once. --The Memento pattern makes it easy to take a snapshot of the state of an instance at a certain time, and even restore it from there. --Although clones may be created to remember the state of all instances, the Memento pattern considers keeping only the information you need and restoring only the data you need.
--Addition ――Adding 1 to 5 ...
1+2+3+4+5=15
――Next, if you add 1 to 10 ...?
--Pattern 1: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55
--Pattern 2: 15 + 6 + 7 + 8 + 9 + 10 = 55
--Pattern 2 is the Memento pattern
--Make this a code# -*- coding:utf-8 -*-
class Memento:
"""Memento class that keeps track of progress"""
result = -1 #Represents the progress of the calculation
def __init__(self, temp):
"""Constructor that receives the calculation progress as an argument"""
Memento.result = temp
class Calc:
"""A class that represents a single calculation."""
temp = 0
@staticmethod
def plus(plus):
"""Method to perform addition"""
Calc.temp += plus
@staticmethod
def create_memento():
"""Method to get progress as Memento"""
return Memento(Calc.temp)
@staticmethod
def set_memento(memento: Memento):
"""Get the calculation progress from Memento and set it to temp"""
Calc.temp = memento.result
@staticmethod
def get_temp():
"""Method to get the calculation result"""
return Calc.temp
class Calculator:
def __init__(self):
self.result_dict = {}
def run(self):
c = Calc()
#1st calculation
for i in range(1, 6):
c.plus(i)
print(f"Addition up to 5: {c.get_temp()}")
self.result_dict["Addition up to 5"] = c.create_memento()
#Second calculation
c2 = Calc()
c2.set_memento(self.result_dict["Addition up to 5"])
#1st calculation
for i in range(6, 11):
c2.plus(i)
print(f"Addition up to 10: {c2.get_temp()}")
self.result_dict["Addition up to 10"] = c2.get_temp()
if __name__ == '__main__':
c = Calculator()
c.run()
――By leaving a certain stage as a "snapshot", you can quickly return to the state at that time. --In the Memento pattern, it is up to the Originator (here Calc) to decide what value should be left as the Memento. --Originator leaves information you think you need as Memento and restores state from Memento
--In object-oriented design, things are often represented as classes. --A pattern that expresses "state" as a class, not a thing
--Conversation at work ――The way you assign tasks changes depending on the mood of your boss (I hate that kind of workplace ...) --When you are in a good mood
Subordinates: Good morning
Boss: Good morning
Subordinates: I'll do xxx today
Boss: Good, good luck
--When you are in a bad mood
Subordinates: Good morning
Boss: Oh
Subordinates: I'll do xxx today
Boss: Hey, did you say ooo? Did you do it?
Subordinate: (I haven't heard ...) I'm sorry, I'll do it soon! !! !! !! !! !! !! !!
--Cord
# -*- coding:utf-8 -*-
class Boss:
STATE_ORDINARY = 0 #Normal boss
STATE_IN_BAD_MOOD = 1 #Boss in a bad mood
def __init__(self):
self.state = -1 #Represents the state of the boss
def change_state(self, state):
"""Change the status of your boss"""
self.state = state
def morning_greet(self):
"""Return the morning greeting"""
if self.state == Boss.STATE_ORDINARY:
return "Good morning"
elif self.state == Boss.STATE_IN_BAD_MOOD:
return "Oh"
else:
pass
def assign_task(self):
"""Shake the task"""
if self.state == Boss.STATE_ORDINARY:
return "Like, do your best"
elif self.state == Boss.STATE_IN_BAD_MOOD:
return "Hey, did you say ooo? Did you do it?"
else:
pass
――Suppose that your boss's boss pointed out and you started to manage more properly → A new pattern was born ――It's not cool to modify the if branch --In the State pattern, prepare a class that represents the "state" and make this "state" interchangeable. --In the case of a sample case, first of all, "good mood diagonal state" and "normal state" are required. --The State can be changed by any class, but in this case, it will be changed from somewhere inside.
# -*- coding:utf-8 -*-
from abc import ABCMeta, abstractmethod
class State(metaclass=ABCMeta):
@staticmethod
def morning_greet():
"""Morning greeting"""
pass
@staticmethod
def assign_task():
"""Shake the task"""
pass
class BadMoodState(State):
@staticmethod
def morning_greet():
return "Oh"
@staticmethod
def assign_task():
return "Hey, did you say ooo? Did you do it?"
class OrdinaryState(State):
@staticmethod
def morning_greet():
return "Good morning"
@staticmethod
def assign_task():
return "Like, do your best"
class StatePatternBoss:
def __init__(self):
self.state = None
def change_state(self, state: State):
self.state = state
def morning_greet(self):
return self.state.morning_greet()
def assign_task(self):
return self.state.assign_task()
if __name__ == '__main__':
boss_state = StatePatternBoss()
print("=====Day 1: Good mood=====")
boss_state.change_state(OrdinaryState())
print("Subordinates: Good morning")
print(f"boss:{boss_state.morning_greet()}")
print("Subordinates: I'll do xxx today")
print(f"boss:{boss_state.assign_task()}")
print("=====Day 2: Bad mood=====")
boss_state.change_state(BadMoodState())
print("Subordinates: Good morning")
print(f"boss:{boss_state.morning_greet()}")
print("Subordinates: I'll do xxx today")
print(f"boss:{boss_state.assign_task()}")
=====Day 1: Good mood=====
Subordinates: Good morning
Boss: Good morning
Subordinates: I'll do xxx today
Boss: Good, good luck
=====Day 2: Bad mood=====
Subordinates: Good morning
Boss: Oh
Subordinates: I'll do xxx today
Boss: Hey, did you say ooo? Did you do it?
――By doing this, it is easy to respond even if the mood pattern increases
(Omission)
class GoodMoodState(State):
@staticmethod
def morning_greet():
return "Good morning! Let's do our best today!"
@staticmethod
def assign_task():
return "How nice! The other ooo was also nice! Good luck with this condition!"
(Omitted)
print("=====Day 3: Good mood=====")
boss_state.change_state(GoodMoodState()) #Just change here
print("Subordinates: Good morning")
print(f"boss:{boss_state.morning_greet()}")
print("Subordinates: I'll do xxx today")
print(f"boss:{boss_state.assign_task()}")
=====Day 3: Good mood=====
Subordinates: Good morning
Boss: Good morning! Let's do our best today!
Subordinates: I'll do xxx today
Boss: Like! The other ooo was also nice! Good luck with this condition!
--Flyweight: means "flyweight" in English --Patterns focused on wasting resources by sharing instances --Example: The small image used for the background of the homepage is not exchanged over the network as many times as it is displayed in the background, but usually the image is acquired once and "the image" is displayed side by side. --A pattern that aims to lighten the entire program by sharing the same instance so as not to create useless instances.
--Try to create a message using "human characters" ―― "Ai is better than blue" ――Make each character with "human characters", take a picture from the roof, and leave it in the picture. --If the class that represents a single human character is the HumanLetter class, the class that generates a message with human characters is as follows.
# -*- coding:utf-8 -*-
class HumanLetter:
def __init__(self, letter):
self._letter = letter
def get_letter(self):
return self._letter
class Main:
@staticmethod
def take_a_photo(letter: HumanLetter):
"""Take a photo"""
print(letter.get_letter())
def main(self):
"""Create a letter"""
a = HumanLetter("Ah")
self.take_a_photo(a)
i = HumanLetter("I")
self.take_a_photo(i)
ha = HumanLetter("Is")
self.take_a_photo(ha)
a2 = HumanLetter("Ah")
self.take_a_photo(a2)
o = HumanLetter("O")
self.take_a_photo(o)
yo = HumanLetter("Yo")
self.take_a_photo(yo)
ri = HumanLetter("Ri")
self.take_a_photo(ri)
mo = HumanLetter("Also")
self.take_a_photo(mo)
a3 = HumanLetter("Ah")
self.take_a_photo(a3)
o2 = HumanLetter("O")
self.take_a_photo(o2)
i2 = HumanLetter("I")
self.take_a_photo(i2)
if __name__ == '__main__':
h = Main()
h.main()
――The same character appears many times. ――If you just take a picture, you can reuse it ...
# -*- coding:utf-8 -*-
class HumanLetter:
def __init__(self, letter):
self._letter = letter
def get_letter(self):
return self._letter
class Main:
@staticmethod
def take_a_photo(letter: HumanLetter):
"""Take a photo"""
print(letter.get_letter())
def main(self):
"""Create a letter"""
a = HumanLetter("Ah")
self.take_a_photo(a)
i = HumanLetter("I")
self.take_a_photo(i)
ha = HumanLetter("Is")
self.take_a_photo(ha)
self.take_a_photo(a)
o = HumanLetter("O")
self.take_a_photo(o)
yo = HumanLetter("Yo")
self.take_a_photo(yo)
ri = HumanLetter("Ri")
self.take_a_photo(ri)
mo = HumanLetter("Also")
self.take_a_photo(mo)
self.take_a_photo(a)
self.take_a_photo(o)
self.take_a_photo(i)
if __name__ == '__main__':
h = Main()
h.main()
--I was able to reduce the number of costly instantiations (here, a constructor that sorts people to form characters. Originally, DB access etc. can be considered). Naturally, the number of instances is also decreasing. --Further reduction with the Flyweight pattern --Create a Factory class that creates and manages instances that should be lightened, and get instances that should be lightened through this Factory class.
# -*- coding:utf-8 -*-
class HumanLetter:
def __init__(self, letter):
self._letter = letter
def get_letter(self):
return self._letter
class HumanLetterFactory:
__singleton = None
__human_letter = None
def __new__(cls, *args, **kwargs):
if cls.__singleton is None:
cls.__singleton = super(HumanLetterFactory, cls).__new__(cls)
return cls.__singleton
def __init__(self):
self.dic = {}
def get_human_letter(self, letter: str):
try:
human_letter = self.dic[letter]
except KeyError:
human_letter = HumanLetter(letter)
self.dic[letter] = human_letter
return human_letter
class Main:
@staticmethod
def take_a_photo(letter: HumanLetter):
"""Take a photo"""
print(letter.get_letter())
def main(self):
"""Create a letter"""
#Create singleton
hlf = HumanLetterFactory()
a = hlf.get_human_letter("Ah")
self.take_a_photo(a)
print(a)
i = hlf.get_human_letter("I")
self.take_a_photo(i)
print(i)
ha = hlf.get_human_letter("Is")
self.take_a_photo(ha)
a2 = hlf.get_human_letter("Ah")
self.take_a_photo(a2)
print(a2)
o = hlf.get_human_letter("O")
self.take_a_photo(o)
print(o)
yo = hlf.get_human_letter("Yo")
self.take_a_photo(yo)
ri = hlf.get_human_letter("Ri")
self.take_a_photo(ri)
mo = hlf.get_human_letter("Also")
self.take_a_photo(mo)
a3 = hlf.get_human_letter("Ah")
self.take_a_photo(a3)
print(a3)
o2 = hlf.get_human_letter("O")
self.take_a_photo(o2)
print(o2)
i2 = hlf.get_human_letter("I")
self.take_a_photo(i2)
print(i2)
if __name__ == '__main__':
h = Main()
h.main()
Ah
<__main__.HumanLetter object at 0x1039c4da0> #It is a common instance in "A"
I
<__main__.HumanLetter object at 0x1039c4dd8>
Is
Ah
<__main__.HumanLetter object at 0x1039c4da0> #It is a common instance in "A"
O
<__main__.HumanLetter object at 0x1039c4e48>
Yo
Ri
Also
Ah
<__main__.HumanLetter object at 0x1039c4da0> #It is a common instance in "A"
O
<__main__.HumanLetter object at 0x1039c4e48>
I
<__main__.HumanLetter object at 0x1039c4dd8>
--By setting the Factory class to Singleton, you can prevent multiple Factory classes from being generated. - Singleton --The get_human_letter method of the HumanLetterFactory class queries the managed dict, and if you need an instance that represents a character you already have, you don't bother to recreate it. --If you are asked for a character that you do not have, instantiate it newly, register it in the map, and return the generated instance. --By using the Flyweight pattern, it is not necessary for the user (caller such as Main class) to know which instance they have. In addition, it is possible to respond to requests from a plurality of callees without waste.
Recommended Posts