LibraryAbout

✈️ Exploring the Flyweight Design Pattern in Ruby


author

Author: David Martinez

Created: June 29, 2024 - Updated: June 30, 2024

Read Time: 12 min

Ruby

Welcome! Today, let's embark on a journey into the Flyweight design pattern. We'll dive deep into its workings, explore real-world scenarios, and unveil its magic!

Unveiling the Flyweight Pattern ✨

The Flyweight is a structural design pattern that allows you to fit more objects into the available amount of RAM by sharing as much data as possible with other similar objects.

It is particularly useful when dealing with a large number of objects that share common properties, optimizing memory usage.

When to Use the Flyweight Pattern?

🔸 Use this pattern when your program needs to support a large number of fine-grained objects efficiently.

🔸 It's beneficial when many objects share common state, which can be extracted and stored externally.

Problem Scenario

Consider developing a shooter video game where particles (such as bullets) are rendered as new objects for each instance. This leads to high memory consumption and eventually crashes the game on devices with limited RAM.

Solution

The Flyweight pattern suggests sharing common state between multiple objects, reducing the memory footprint. For our shooter game, we can extract the common properties of particles and share them across instances.

UML Diagram

In this example, the Flyweight pattern is utilized to manage the particles in a shooter game.

🔹 The Flyweight interface declares methods that the shared and unique parts of the state will implement.

🔹 Concrete Flyweight objects implement the Flyweight interface and store the shared state.

🔹 The Flyweight Factory manages the Flyweight objects and ensures the reuse of shared objects.

🔹 Client code uses the Flyweight objects to render particles efficiently.

How does it Work?

1️⃣ Extract the common properties of the particles and store them in shared Flyweight objects.

2️⃣ The Flyweight Factory manages these shared objects, ensuring reuse rather than creating new instances.

3️⃣ The client code uses Flyweight objects to render particles, passing unique properties as needed.

💡 Note: The Flyweight pattern can significantly reduce memory consumption by sharing common data between objects.

Why Embrace the Flyweight Pattern?

🔮 Optimizes memory usage by sharing common data between multiple objects.

🔮 Reduces the overhead of object creation, enhancing performance.

🔮 Facilitates managing a large number of objects efficiently.

🔮 Aligns with the principle of least astonishment by minimizing memory footprint.

🔮 Encourages the separation of intrinsic (shared) and extrinsic (unique) state, promoting cleaner code.

Show me the code

# Flyweight interface
class ParticleFlyweight
def render(x, y)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Concrete Flyweight
class BulletParticle < ParticleFlyweight
def initialize(image)
@image = image
end
def render(x, y)
puts "Rendering Bullet at (#{x}, #{y}) with image #{@image}"
end
end
# Flyweight Factory
class ParticleFactory
def initialize
@particles = {}
end
def get_particle(image)
@particles[image] ||= BulletParticle.new(image)
end
end
# Client code
class Game
def initialize(factory)
@factory = factory
@particles = []
end
def create_bullet(x, y, image)
particle = @factory.get_particle(image)
@particles << { particle: particle, x: x, y: y }
end
def render
@particles.each do |p|
p[:particle].render(p[:x], p[:y])
end
end
end
# Example Usage
def main
factory = ParticleFactory.new
game = Game.new(factory)
game.create_bullet(10, 20, 'bullet.png')
game.create_bullet(15, 25, 'bullet.png')
game.create_bullet(20, 30, 'bullet.png')
game.render
end
main
# Output
# Rendering Bullet at (10, 20) with image bullet.png
# Rendering Bullet at (15, 25) with image bullet.png
# Rendering Bullet at (20, 30) with image bullet.png

Conclusion 🔖

The Flyweight pattern allows efficient memory usage by sharing as much data as possible with other similar objects.

🔺 This pattern is advantageous when dealing with a large number of fine-grained objects that share common properties.

🔺 It's beneficial when optimizing memory consumption and enhancing performance.

🔺 The Flyweight pattern aligns with the principle of least astonishment by minimizing memory footprint and promoting cleaner code.

🔺 By embracing the Flyweight pattern, you can efficiently manage a large number of objects while optimizing memory usage.

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 📚