Design Patters: Prototype

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 .

Leave a comment