Flyweight Pattern

The Flyweight design pattern is a structural pattern designed to minimize memory usage by sharing as much data as possible with other similar objects. It divides an object's data into two categories: intrinsic state(heavy, constant, and shareable data) and extrinsic state (lightweight, unique, context-specific data)
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <memory>
#include <shared_mutex>

// 1. INTRINSIC STATE (The Flyweight)
// This class contains heavy, immutable data shared across thousands of instances.
class TreeModel {
public:
    TreeModel(std::string name, std::string color, std::vector texture_data)
        : name_(std::move(name)), color_(std::move(color)), texture_bytes_(std::move(texture_data)) {
        std::cout << "[Cache Miss] Created heavy TreeModel: " << name_ << " (" << color_ << ")\n";
    }

    void render(double x, double y) const {
        std::cout << "Rendering " << name_ << " [" << color_ << "] at coordinates (" << x << ", " << y << ")\n";
    }

private:
    std::string name_;
    std::string color_;
    std::vector texture_bytes_; // Simulates heavy mesh/texture data
};

// 2. FLYWEIGHT FACTORY
// Manages the shared pool of flyweights. It ensures objects are safely reused.
class TreeFactory {
public:
    // Thread-safe lookup and retrieval using std::shared_ptr
    std::shared_ptr get_tree_type(std::string_view name, std::string_view color) {
        // Create a unique compound key for our map lookup
        std::string key = std::string(name) + "_" + std::string(color);

        // Reader lock: Multiple threads can search the cache simultaneously
        {
            std::shared_lock lock(cache_mutex_);
            if (auto it = cache_.find(key); it != cache_.end()) {
                return it->second;
            }
        }

        // Writer lock: Only one thread can insert a new model if it's a cache miss
        std::unique_lock lock(cache_mutex_);
        
        // Double-checked locking pattern to handle race conditions
        auto& ptr = cache_[key];
        if (!ptr) {
            // Mocking heavy texture bytes allocation
            std::vector mock_texture(1024 * 1024, 0xFF); 
            ptr = std::make_shared(std::string(name), std::string(color), std::move(mock_texture));
        }
        return ptr;
    }

    size_t get_pool_size() const {
        std::shared_lock lock(cache_mutex_);
        return cache_.size();
    }

private:
    mutable std::shared_mutex cache_mutex_;
    std::unordered_map> cache_;
};

// 3. EXTRINSIC STATE & CONTEXT
// Contains unique properties (coordinates) and links back to the shared intrinsic state.
class Tree {
public:
    Tree(double x, double y, std::shared_ptr model)
        : x_(x), y_(y), model_(std::move(model)) {}

    void draw() const {
        // Delegates behavior to the intrinsic flyweight object, passing extrinsic context
        model_->render(x_, y_);
    }

private:
    double x_; // Extrinsic state
    double y_; // Extrinsic state
    std::shared_ptr model_; // Pointer to shared Intrinsic state
};

// 4. CLIENT CODE
class Forest {
public:
    void plant_tree(double x, double y, std::string_view name, std::string_view color) {
        auto model = factory_.get_tree_type(name, color);
        trees_.emplace_back(x, y, std::move(model));
    }

    void draw() const {
        for (const auto& tree : trees_) {
            tree.draw();
        }
    }

    size_t total_unique_models() const { return factory_.get_pool_size(); }
    size_t total_rendered_trees() const { return trees_.size(); }

private:
    TreeFactory factory_;
    std::vector trees_;
};

int main() {
    Forest forest;

    // Planting multiple trees; many share the same type configurations
    forest.plant_tree(10.5, 20.3, "Oak", "Green");
    forest.plant_tree(15.1, 40.2, "Oak", "Green");  // Reuses the instance above
    forest.plant_tree(100.0, 200.0, "Pine", "Dark Green");
    forest.plant_tree(50.2, 89.1, "Oak", "Green");  // Reuses the instance above
    forest.plant_tree(102.4, 204.8, "Pine", "Dark Green"); // Reuses the instance above

    std::cout << "\n--- Forest Statistics ---\n";
    std::cout << "Total trees planted: " << forest.total_rendered_trees() << "\n";
    std::cout << "Total allocated Flyweight models in RAM: " << forest.total_unique_models() << "\n\n";

    std::cout << "--- Rendering Forest ---\n";
    forest.draw();
}

Comments

Popular posts from this blog

BdsDex: failed to load Boot0001 "UEFI BHYVE SATA DISK BHYVE-OABE-20A5-E582" from PciRoot(0x0)/Pci (0x2, 0x0)/Stat(0x0,0xFFFF,0x0) : Not Found

FFmpeg