---
title: "CP4 - Prototype - Learn Design Pattern From Simple Things"
description: "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."
pubDate: "2022-12-18"
lang: "en"
slug: "cp4-prototype-learn-design-pattern-from-simple-things"
url: "https://hoangyell.com/cp4-prototype-learn-design-pattern-from-simple-things/"
author: "HoangYell"
categories: ["Software Craft"]
tags: ["design-patterns", "creational-patterns"]
heroImage: "https://raw.githubusercontent.com/HoangGeek/store/main/webp/syNmmBu.webp"
---

_Prototype is a **creational** design pattern:_
🖤🤍🤍🤍🤍

## What is Prototype?

Prototype is a technique for making the original object copyable.

![Image](https://raw.githubusercontent.com/HoangGeek/store/main/webp/208253852-a6e3f152-9846-4855-8161-ef23abe97c2e.webp)

## 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 `copy` to do that.
  - Since the built-in module `copy` can clone any object in Python, any object can be a Prototype object.
  - The example of override `def __deepcopy__` in [refactoring.guru](https://refactoring.guru/design-patterns/prototype/python/example) is complicated and incorrect. Don't copy them. You just need to use the copy module as usual.

**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._

![Image](https://y.yarn.co/c7731c16-7490-416e-84e8-9c6b5158b71d_text.gif)

## 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_robot` from original object `original_robot`:
    - Coping by (shallow)copy:
      - copied_robot.mutable ↔️ original_robot.mutable
    - Coping by deep copy:
      - copied_robot.mutable 🛡️ original_robot.mutable

[learn more about mutable and immutable](https://www.mygreatlearning.com/blog/understanding-mutable-and-immutable-in-python)

``` python
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:

``` shell
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:
``` python
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 ID `402`

```shell
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](https://github.com/HoangYell/Design-Pattern-From-Simple-Things/blob/main/cp4_prototype_refactoring_guru.py)

### Prototype Implementation:
You’re already using Prototype from **Input:**
``` python
# nothing here
```

[Source Code: Correct Implement](https://github.com/HoangYell/Design-Pattern-From-Simple-Things/blob/main/cp4_prototype.py)
