Credit: Concepts explained here are based on a book, “増補改訂版Java言語で学ぶデザインパターン入門”.
Prototype is a pattern in which an instance is duplicated by calling an Interface, not by calling a constructor. This pattern is used when it is convenient to clone an instance than to create a new instance.
Let’s explain concepts in a program.
First, we define Product interface. Note that copy method shallow-copies its instance itself.
# product.py
from abc import ABC, abstractmethod
class Product(ABC):
@abstractmethod
def use(self, s):
pass
@abstractmethod
def create_clone(self):
pass
def __copy__(self):
return self
Next, we define Manager class which calls Product interface to clone an instance. In this class, no specific class is defined such as a class representing a concrete product.
# manager.py
from framework.product import Product
class Manager:
_showcase = dict()
def register(self, name, proto):
self._showcase[name] = proto
def create(self, protoname):
p = self._showcase.get(protoname)
return p.create_clone()
Let’s define two classes that extend Product interface.
The first one is MessageBox class which encapsulates a string with a given character and prints it out. The second one is UnderlinePen which underlies a string with a given character and prints it out.
# message_box.py
from framework import Product
from copy import copy
class MessageBox(Product):
def __init__(self, decochar):
self._decochar = decochar
def use(self, s):
length = len(s.encode('utf-8'))
for _ in range(0, length+4):
print(self._decochar, end="")
print("")
print(f"{self._decochar} {s} {self._decochar}")
for _ in range(0, length+4):
print(self._decochar, end="")
print("")
def create_clone(self):
p = copy(self)
return p
# underline_pen.py
from framework import Product
from copy import copy
class UnderlinePen(Product):
def __init__(self, ulchar):
self._ulchar = ulchar
def use(self, s):
length = len(s.encode('utf-8'))
print(f"{ s }")
# print(" ")
for _ in range(0, length+4):
print(self._ulchar, end="")
print("")
def create_clone(self):
p = copy(self)
return p
Let’s run this program.
# main.py
from framework import Manager
from framework import Product
from message_box import MessageBox
from underline_pen import UnderlinePen
from copy import copy
def main():
manager = Manager()
upen = UnderlinePen('~')
mbox = MessageBox('*')
sbox = MessageBox('/')
manager.register("strong message", upen)
manager.register("warning box", mbox)
manager.register("slash box", sbox)
p1 = manager.create("strong message")
assert isinstance(p1, Product)
p1.use("Hello, world.")
p2 = manager.create("warning box")
assert isinstance(p2, Product)
p2.use("Hello, world.")
p3 = manager.create("slash box")
assert isinstance(p3, Product)
p3.use("Hello, world.")
if __name__ == "__main__":
main()
Hello, world.
~~~~~~~~~~~~~~~~~
*****************
* Hello, world. *
*****************
/////////////////
/ Hello, world. /
/////////////////
One benefit of a prototype pattern: When it is costly to create an instance the second time, it is efficient to copy an instance. For example, an instance is created after logical operation to database which involves IO cost and operation cost. Once an instance is created, it can be stored in a cache and the instance can be cloned the next time you want to use that instance. Here is a good article about this design’s benefit .