Design Pattern (9) Prototype Pattern Complete Tutorial - Object Cloning and Performance Optimization

You can download the Design Pattern series code from this design_pattern repo.

Preface

In our journey through Creational Design Patterns, we’ve explored Factory Method, Abstract Factory, Builder, and other patterns. Today we’ll introduce the Prototype Pattern, which primarily solves object copying problems.

This pattern reminds me of a music light show editing app I developed in the past. At that time, I hadn’t learned about design patterns yet, so I didn’t use patterns to handle related functionality. Looking back now, I realize that functional scenario was very suitable for applying the Prototype Pattern.

This is an app for editing music light shows. Interested readers can download it and try it out 🙂

Requirements Background

During the development of the music light show app, I received feedback from clients. They indicated that after editing one light sequence, they still needed to edit six other similar light sequences, which was quite time-consuming.

Clients hoped to add Copy & Paste functionality, allowing them to quickly generate new sequences by copying already-edited light sequences and making minor adjustments, greatly saving editing time. The specific workflow is shown in the following diagrams:

Initial Design Analysis (OOA)

After understanding the requirements, let’s conduct object-oriented analysis to design a simple and intuitive solution.

In the most direct approach, when we need to copy LightShowData, we can use the same jsonObject data to recreate a new LightShowData instance. This approach seems simple, but it actually brings some problems.

Problem Analysis (Forces)

Through initial analysis, we can discover the following key problems with the above simple design:

Complexity Issues

If the LightShowData constructor becomes complex, requiring many parameters, then every copy would need to understand all internal implementation details. This violates the encapsulation principle and increases code coupling.

Performance Issues

Suppose the constructor needs to perform complex calculations or data processing during instance creation (such as music beat analysis, lighting effect calculations, etc.). Then every time we create a new instance, we would repeat these time-consuming operations, seriously affecting program performance.

These problems drive us to seek a more elegant solution.

Prototype Pattern Solution

After identifying the core problems, we can apply the Prototype Pattern to solve these challenges. The core idea of the Prototype Pattern is “create new objects by copying existing objects” rather than reconstructing them.

Pattern Structure

Let’s first look at the standard UML structure of the Prototype Pattern:

Core Role Explanations

The Prototype Pattern mainly includes the following two key roles:

1. Prototype (Prototype Interface)

This is an abstract interface or abstract class that defines the standard method for copying itself (usually the clone() method). The main purpose of this interface is to provide a unified copying specification, allowing clients to create object copies without needing to know the specific object class.

2. Concrete Prototype

Concrete classes that implement the prototype interface. These classes must implement the clone() method, responsible for creating exact copies of themselves. During implementation, it’s necessary to ensure that newly created objects are exactly the same in state as the original objects, but are completely independent entities in memory.

Applying to Light Show Application

Now let’s apply the Prototype Pattern to the LightShow App design:

By introducing the Prototype Pattern, we’ve redesigned the system architecture. LightShowData now implements the LightShowDataPrototype interface, providing the clone() method for efficient object copying. This design gives us a more flexible and efficient new architecture.

Program Implementation (OOP)

After understanding the design structure, let’s proceed with concrete code implementation. Let’s gradually build a complete Prototype Pattern implementation:

[LightShowDataPrototype]

interface LightShowDataPrototype {
    val startIndex: Int
    val lightDataList: List<Int>
    fun clone(): LightShowDataPrototype
}

[LightShowData]

package prototypepattern.source

class LightShowData: LightShowDataPrototype {

    override val startIndex: Int
    override val lightDataList: List<Int>

    constructor(originalDataList: List<Int>) {
        startIndex = originalDataList[0]
        lightDataList = originalDataList.subList(1, originalDataList.size).map { it * 2 }
    }

    constructor(startIndex: Int, lightDataList: List<Int>) {
        this.startIndex = startIndex
        this.lightDataList = lightDataList
    }

    override fun clone(): LightShowDataPrototype {
        return LightShowData(startIndex, lightDataList.toList())
    }
}

[main]

fun main() {
    val originalData = listOf(1, 2, 3, 4, 5)

    // Before using prototype pattern
    val originalLightShowData: LightShowDataPrototype = LightShowData(originalData)
    val newLightShowData: LightShowDataPrototype = LightShowData(originalData)

    println(originalLightShowData)
    println(newLightShowData)

    // After using prototype pattern
    val clonedLightShowData: LightShowDataPrototype = LightShowData(originalData)

    println(originalLightShowData)
    println(clonedLightShowData)
}

Performance Advantage Analysis

Through the above implementation, we can clearly see the advantages of the Prototype Pattern. When using the clone() method for copying, we can avoid repeatedly executing time-consuming initialization logic:

originalDataList.subList(1, originalDataList.size).map { it * 2 }

This line of code represents complex light data processing logic. In the original design, every new object creation required recalculation. But through the Prototype Pattern, we only need to calculate once during the first creation, and subsequent copies can directly reuse the processed data, greatly improving program performance.

Pattern Summary

The Prototype Pattern is the last important member of Creational Design Patterns. It solves performance problems in complex object creation through object copying, particularly suitable for:

  • Scenarios where object creation costs are high
  • Situations where complex constructor parameters need to be avoided
  • Cases where similar objects need to be created

At this point, we’ve completed learning all Creational Design Patterns. The next phase will enter exploration of Structural Design Patterns, learning how to elegantly combine objects and classes to create more flexible system architectures.




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Claude Code 使用技巧與最佳實踐 - Tips and Best Practices
  • 🤖 AI Agent Series (Part 1): Understanding the Core Interaction Logic of LLM, RAG, and MCP
  • 💡 Managing Multiple GitHub Accounts on One Computer: The Simplest SSH Configuration Method
  • 🚀 How to Use Excalidraw AI to Quickly Generate Professional Diagrams and Boost Work Efficiency!
  • Complete macOS Development Environment Setup Guide: Mobile Development Toolchain Configuration Tutorial