LibraryAbout

🔄 Unlocking the Prototype Design Pattern in Ruby


author

Author: David Martinez

Created: November 15, 2024 - Updated: November 16, 2024

Read Time: 10 min

Ruby

In this article, we’ll dive into the Prototype design pattern, a flexible and efficient way to create copies of existing objects. We'll break down its structure, advantages, and demonstrate a practical use case in Ruby. This marks the conclusion of the creational design patterns series; moving forward, we’ll explore structural design patterns.

Introducing the Prototype Pattern 🧬

The Prototype pattern is a creational design pattern that focuses on cloning objects instead of creating them from scratch. By duplicating existing objects, the pattern minimizes the cost and complexity of initialization.

This pattern shines in scenarios where object creation is expensive or when object configurations are challenging to replicate manually.

When to Use the Prototype Pattern?

🔹 Use Prototype when object creation is resource-intensive, such as parsing data, initializing complex structures, or setting up expensive configurations.

🔹 It’s especially useful when the structure of an object is dynamic or when creating objects from existing configurations is more efficient than building them from scratch.

Problem Scenario

Imagine a graphics application that involves multiple shapes such as rectangles, circles, and polygons. Each shape may have specific configurations, including size, position, and color. Copying these shapes manually becomes error-prone and repetitive.

For example:

  • A rectangle might need its width, height, and color set.
  • A circle might need its radius and color configured.

Manually re-creating these shapes every time wastes resources and risks introducing inconsistencies.

Solution

The Prototype pattern enables us to duplicate existing shapes efficiently by defining a cloning interface. This eliminates the need for manual configuration, making the code cleaner and easier to maintain.

UML Diagram

🔹 The base class Shape provides the structure and cloning interface.

🔹 Concrete classes like Rectangle and Circle implement the cloning process to copy their properties effectively.

How does it Work?

1️⃣ Define a prototype interface with a clone method.

2️⃣ Implement the interface in concrete classes to handle cloning of specific attributes.

3️⃣ Use the prototype objects to duplicate existing configurations, ensuring consistency and efficiency.

Why Embrace the Prototype Pattern?

🔮 Efficiently creates new objects by copying existing ones.

🔮 Reduces initialization complexity by leveraging predefined configurations.

🔮 Promotes modular code by encapsulating cloning logic in a single interface.

🔮 Simplifies extending object hierarchies, as new classes inherit the cloning behavior.

Show me the code

require 'ostruct'
# Base prototype class
class Shape
def initialize(source)
@x = source.x
@y = source.y
@color = source.color
end
def clone
raise NotImplementedError, 'You should implement this method'
end
end
# Concrete prototype: Rectangle
class Rectangle < Shape
def initialize(source)
super(source)
@width = source.width
@height = source.height
end
def clone
Rectangle.new(OpenStruct.new(x: @x, y: @y, width: @width, height: @height, color: @color))
end
end
# Concrete prototype: Circle
class Circle < Shape
def initialize(source)
super(source)
@radius = source.radius
end
def clone
Circle.new(OpenStruct.new(x: @x, y: @y, radius: @radius, color: @color))
end
end
# Client code
def main
shapes = []
shapes.push(Rectangle.new(OpenStruct.new(x: 10, y: 20, width: 50, height: 60, color: 'red')))
shapes.push(Circle.new(OpenStruct.new(x: 10, y: 20, radius: 50, color: 'blue')))
# Cloning shapes
shapes_copy = []
shapes.each do |shape|
shapes_copy.push(shape.clone)
end
puts 'Shapes:'
puts shapes.inspect
puts 'Shapes copy:'
puts shapes_copy.inspect
end
main if __FILE__ == $PROGRAM_NAME
# Output:
# Shapes:
# [#<Rectangle:0x00007fcb888c1300>, #<Circle:0x00007fcb888c1218>]
# Shapes copy:
# [#<Rectangle:0x00007fcb888c0fa8>, #<Circle:0x00007fcb888c0e30>]

Conclusion 🔖

The Prototype pattern is a powerful solution for creating object copies without the overhead of initialization.

🔺 It’s ideal for duplicating objects with complex configurations or expensive initialization.

🔺 By encapsulating cloning logic, the Prototype pattern ensures consistency and simplifies code maintenance.

🔺 Using this pattern, developers can create scalable, efficient systems with reusable object configurations.

Join the Quest!

  • Design Patterns: Elements of Reusable Object-Oriented Software
  • Head First Design Patterns: A Brain-Friendly Guide
  • Code example

💻 You can find this and other design patterns here 📚