Prototype Design Pattern

Name: Prototype Design Pattern

Type: Creational Design Pattern

Purpose:
  • Improving performance by cloning objects.
  • Minimize complexity in object creation.

Sample Problem and Solution:
Consider a problem where you need a 64x64 grid Board class to represent a chess board. A possible design is provided here.
public class Cell {
    private String color;

    public Cell(String color) {
        this.color = color;

        // Make it time consuming task.
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }

    public String getColor() {
        return color;
    }

    @Override
    public String toString() {
        return color.substring(0, 1);
    }
}
In this code, Thread.sleep is used to make the object creation as a time consuming operation.
public class Board {
    public static final int NO_OF_ROWS = 8;
    public static final int NO_OF_COLUMNS = 8;

    private final Cell[][] board;

    public Board() {
        this.board = new Cell[NO_OF_ROWS][NO_OF_COLUMNS];

        for (int row = NO_OF_ROWS - 1; row >= 0; row--) {
            for (int col = NO_OF_COLUMNS - 1; col >= 0; col--) {
                if ((row + col) % 2 == 0) {
                    board[row][col] = new Cell("WHITE");
                } else {
                    board[row][col] = new Cell("BLACK");
                }
            }
        }
    }

    public void print() {
        for (int row = 0; row < NO_OF_ROWS; row++) {
            for (int col = 0; col < NO_OF_COLUMNS; col++) {
                System.out.print(board[row][col] + " ");
            }
            System.out.println();
        }
    }
}
public class Main {

    public static void main(String[] args) {
        // Get the start time
        long startTime = System.currentTimeMillis();

        Board chessBoard = new Board();

        // Get the end time
        long endTime = System.currentTimeMillis();

        System.out.println("Time taken to create a board: " + (endTime - startTime) + " millis");

        // Print the board
        chessBoard.print();
    }

}

Suppose you want to create another board object for checkers, what will you do? Obviously creating another similar object will almost double the execution time. However Prototype Design Pattern provides a perfect solution for it. Prototype design patterns recommends cloning an existing object to create a copy of it, instead of creating a new object using the time consuming constructor.

Lets modify the Cell class and Board class by implementing java.lang.Cloneable interface and overriding clone method from the java.lang.Object class. Remember that the Cloneable interface is a marker interface (An interface without any methods). We are overriding the clone method from the java.lang.Object class not from the java.lang.Clonable interface.

Modified Cell class:
public class Cell implements Cloneable {
    private String color;

    public Cell(String color) {
        this.color = color;

        // Make it time consuming task.
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }

    public String getColor() {
        return color;
    }

    @Override
    public String toString() {
        return color.substring(0, 1);
    }

    @Override
    public Object clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
}

Modified Board class:
public class Board implements Cloneable {
    public static final int NO_OF_ROWS = 8;
    public static final int NO_OF_COLUMNS = 8;

    private final Cell[][] board;

    public Board() {
        this.board = new Cell[NO_OF_ROWS][NO_OF_COLUMNS];

        for (int row = NO_OF_ROWS - 1; row >= 0; row--) {
            for (int col = NO_OF_COLUMNS - 1; col >= 0; col--) {
                if ((row + col) % 2 == 0) {
                    board[row][col] = new Cell("WHITE");
                } else {
                    board[row][col] = new Cell("BLACK");
                }
            }
        }
    }

    public void print() {
        for (int row = 0; row < NO_OF_ROWS; row++) {
            for (int col = 0; col < NO_OF_COLUMNS; col++) {
                System.out.print(board[row][col] + " ");
            }
            System.out.println();
        }
    }

    @Override
    public Object clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
}
Now create a chess board as usual, but once it is created do not waste the time in creating another Board object using constructor. Use the clone method of the existing Board object.

Modified Main class:
public class Main {

    public static void main(String[] args) {
        // Get the start time
        long startTime = System.currentTimeMillis();

        Board chessBoard = new Board();

        // Get the end time
        long endTime = System.currentTimeMillis();

        System.out.println("Time taken to create a board: " + (endTime - startTime) + " millis");

        // Print the board
        chessBoard.print();

        System.out.println();

        // Get the start time
        startTime = System.currentTimeMillis();

        // Clone the object
        Board checkersBoard = (Board) chessBoard.clone();

        // Get the end time
        endTime = System.currentTimeMillis();

        System.out.println("Time taken to clone a board: " + (endTime - startTime) + " millis");

        checkersBoard.print();
    }

}
This code took around 6.5 seconds to create a new object and 0 milliseconds to clone that object in my Core i3 Ubuntu_64 bit machine. As you can see, the Prototype design pattern provides a huge performance improvement compared to the traditional way of creating an object. But you may argue that we are delaying the object creation purposefully (Using Thread.sleep). That's right! but there are situations where you may experience similar time delay when creating an object. Suppose if we need to read a file from the hard disk to create an object, we will face a similar kind of performance problem.

To make it more clear, assume that you are working on a large list of integers and now you need to create a copy of this list for some strange reasons. If you use a for loop to copy the values from one list to another, it will reduce the performance of your application. You can save the time by simply cloning the entire list into a new one.

Also you have the freedom to modify the cloned object inside the clone method or later. For example, you can remove or add few integers in the cloned list of integers.

Quick recipe:
  1. Implement the java.lang.Clonable interface.
  2. Override the clone method.
  3. Clone the object, whenever it is required.
Find the source codes at Git Hub.
Previous
Next Post »

Contact Form

Name

Email *

Message *