CP4 - Prototype - Learn Design Pattern From Simple Things
Having been with you for a long time, your robot has memories that regular manufacturing can't produce, now you want to clone it. Prototype will be your remedy.
Prototype is a creational design pattern: 🖤🤍🤍🤍🤍
What is Prototype?
Prototype is a technique for making the original object copyable.
Why use Prototype?
- Just to copy an object.
- You don’t need to learn Prototype in Python, because we already have a built-in module
copyto do that.- Since the built-in module
copycan clone any object in Python, any object can be a Prototype object. - The example of override
def __deepcopy__in refactoring.guru is complicated and incorrect. Don’t copy them. You just need to use the copy module as usual.
- Since the built-in module
Question: So… what is the purpose of this blog?
Answer: Just for knowing that Prototype is not necessary to learn in Python, now skip to another pattern.
When to use Prototype?
Question: When do I use Prototype?
Answer: When you use copy.copy or copy.deep_copy, you’re already using Prototype.
Input:
- Having an original object
Robot - Create the copy
copied_robotfrom original objectoriginal_robot:- Coping by (shallow)copy:
- copied_robot.mutable ↔️ original_robot.mutable
- Coping by deep copy:
- copied_robot.mutable 🛡️ original_robot.mutable
- Coping by (shallow)copy:
learn more about mutable and immutable
import copy
class MemoryRobot:
def __init__(self):
self.who_am_i = None
def set_memory(self, who_am_i):
self.who_am_i = who_am_i
class Robot:
def __init__(self, age, body_parts, memory):
self.age = age
self.body_parts = body_parts
self.memory = memory
@property
def id(self):
# https://www.digitalocean.com/community/tutorials/python-id
return str(id(self))[-3:]
if __name__ == "__main__":
def init_robot():
robot_body_parts = [
"head",
{"left hand", "chest", "right hand"},
["left foot", "butt", "right foot"],
]
memory_robot = MemoryRobot()
robot = Robot(18, robot_body_parts, memory_robot)
memory_robot.set_memory(robot)
return robot
def copy_robot(origin_robot, copy_method):
wing = "wing"
tail = "tail"
tab = " "
tab_2 = " " * 2
tab_3 = " " * 3
print(f"\nCoping by {copy_method.__name__}:")
copied_robot = copy_method(origin_robot)
copied_robot.body_parts.append(tail)
is_origin_robot_changed = origin_robot.body_parts[-1] == tail
print(f"{tab}Question: update copied_robot changes origin_robot?")
print(f"{tab_2}Answer: {is_origin_robot_changed}")
origin_robot.body_parts[1].add(wing)
is_copied_robot_changed = wing in copied_robot.body_parts[1]
print(f"{tab}Question: update origin_robot changes copied_robot?")
print(f"{tab_2}Answer: {is_copied_robot_changed}")
print(f"{tab}How about reference?")
print(f"{tab_2}origin_robot reference:")
print(f"{tab_3}{origin_robot.id} is the ID of origin_robot")
print(
f"{tab_3}{origin_robot.memory.who_am_i.id} is the ID of origin_robot.memory.who_am_i"
)
print(f"{tab_2}copied_robot reference:")
print(f"{tab_3}{copied_robot.id} is the ID of copied_robot")
print(
f"{tab_3}{copied_robot.memory.who_am_i.id} is the ID of copied_robot.memory.who_am_i"
)
return copied_robot
origin_robot_1 = init_robot()
shallow_copied_robot = copy_robot(origin_robot_1, copy.copy)
origin_robot_2 = init_robot()
deep_copied_robot = copy_robot(origin_robot_2, copy.deepcopy)
Expected Output:
Coping by copy:
Question: update copied_robot changes origin_robot?
Answer: True
Question: update origin_robot changes copied_robot?
Answer: True
How about reference?
origin_robot reference:
100 is the ID of origin_robot
100 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
101 is the ID of copied_robot
100 is the ID of copied_robot.memory.who_am_i
Coping by deepcopy:
Question: update copied_robot changes origin_robot?
Answer: False
Question: update origin_robot changes copied_robot?
Answer: False
How about reference?
origin_robot reference:
200 is the ID of origin_robot
200 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
201 is the ID of copied_robot
201 is the ID of copied_robot.memory.who_am_i
How to implement Prototype?
Refactoring.guru-Prototype implementation:
class Robot:
...
def __copy__(self):
body_parts = copy.copy(self.body_parts)
memory = copy.copy(self.memory)
copied_robot = self.__class__(self.age, body_parts, memory)
copied_robot.__dict__.update(self.__dict__)
return copied_robot
def __deepcopy__(self, _=None):
if _ is None:
_ = {}
body_parts = copy.deepcopy(self.body_parts, _)
memory = copy.deepcopy(self.memory, _)
copied_robot = self.__class__(self.age, body_parts, memory)
copied_robot.__dict__ = copy.deepcopy(self.__dict__, _)
return copied_robot
...
This implement is incorrect because:
- in the deep copy:
How about reference?(output line#24!=#25)- copied_robot ID
401!= copied_robot.memory.who_am_i ID402
- copied_robot ID
Coping by copy:
Question: update copied_robot changes origin_robot?
Answer: True
Question: update origin_robot changes copied_robot?
Answer: True
How about reference?
origin_robot reference:
300 is the ID of origin_robot
300 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
301 is the ID of copied_robot
300 is the ID of copied_robot.memory.who_am_i
Coping by deepcopy:
Question: update copied_robot changes origin_robot?
Answer: False
Question: update origin_robot changes copied_robot?
Answer: False
How about reference?
origin_robot reference:
400 is the ID of origin_robot
400 is the ID of origin_robot.memory.who_am_i
copied_robot reference:
401 is the ID of copied_robot
402 is the ID of copied_robot.memory.who_am_i
Source Code: Incorrect Implement
Prototype Implementation:
You’re already using Prototype from Input:
# nothing here
Related posts
-
Verify vs Cert: The Python Requests Handbook
Understanding SSL/TLS in Python Requests: The 'verify' and 'cert' arguments explained with interactive animations.
-
SP7 - Proxy - Learn Design Pattern From Simple Things
The boss wants the employees to focus on work and not get distracted by social media. So he decides to block some websites on the corporate network during working hours.
-
SP6 - Object Pool - Learn Design Pattern From Simple Things
Producing planes on a large scale is expensive, but fortunately the manufactured raw parts are always stored in the pool, thereby reducing duplication in the production process.
-
SP5 - Facade - Learn Design Pattern From Simple Things
There are many departments in the building and you feel confused! By opening the entrances from the facade according to purposes, you simply follow the pre-arranged flow.