Imagen destacada del tutorial: Patrones de Diseño de Comportamiento en C++
Patrones de Diseño

Patrones de Diseño de Comportamiento en C++

José Elías Romero Guanipa
02 Sep 2025

Aprende patrones de diseño de comportamiento como Observer, Strategy, Command, State, Template Method, Iterator, Mediator y Memento con ejemplos prácticos en C++. Descubre cómo implementar estos patrones aprovechando las fortalezas del lenguaje C++ como type safety, performance y memory management.

patrones diseño patrones comportamiento observer strategy command +10 más

¡Domina los patrones de diseño de comportamiento en C++! En este tutorial especializado te guiaré paso a paso para que aprendas los patrones fundamentales de comportamiento, incluyendo Observer, Strategy, Command, State, Template Method, Iterator, Mediator y Memento, con ejemplos prácticos y casos de uso reales implementados en C++ moderno.

Objetivo: Aprender los patrones de diseño de comportamiento más importantes, sus implementaciones en C++, ventajas, desventajas y cuándo aplicarlos para gestionar algoritmos, estados y comunicaciones entre objetos, aprovechando las fortalezas únicas del lenguaje C++.

¿Por qué C++ para patrones de comportamiento?

C++ es especialmente adecuado para implementar patrones de comportamiento porque ofrece un control y eficiencia que otros lenguajes no pueden igualar:

  • Type Safety y Zero-cost Abstractions: El sistema de tipos fuerte de C++ detecta errores en tiempo de compilación, y los patrones bien diseñados no añaden overhead en runtime gracias al inlining y optimizaciones del compilador
  • Performance Superior: Los patrones se ejecutan con overhead mínimo, ideal para aplicaciones críticas donde cada microsegundo cuenta
  • Memory Management Avanzado: Control preciso sobre la memoria con RAII (Resource Acquisition Is Initialization) y smart pointers, permitiendo implementar patrones como Memento y Command de manera segura y eficiente
  • Multi-threading Nativo: Soporte integrado para concurrencia en patrones como Observer, con herramientas como mutex, atomic y memory models para thread safety
  • Templates Metaprogramming: Permiten implementar patrones genéricos y reutilizables, con type safety en tiempo de compilación y optimizaciones automáticas
  • Encapsulation Estricta: Control total sobre el acceso a miembros de clase, permitiendo implementar patrones como State y Strategy con verdadera encapsulación
  • STL Integration: Muchos patrones ya están implementados eficientemente en la Standard Template Library, y los patrones personalizados pueden integrarse perfectamente
  • Move Semantics y RAII: Permiten implementar patrones con máxima eficiencia, transfiriendo ownership de recursos sin copias innecesarias

Índice

Paso 1: ¿Qué son los patrones de comportamiento?

Los patrones de diseño de comportamiento se centran en la comunicación entre objetos y la asignación de responsabilidades entre ellos. Estos patrones ayudan a definir cómo los objetos interactúan y se distribuyen las responsabilidades.

¿Por qué son importantes?

  • Flexibilidad: Permiten cambiar el comportamiento en tiempo de ejecución
  • Desacoplamiento: Reducen las dependencias entre objetos
  • Reutilización: Facilitan la composición de comportamientos
  • Mantenimiento: Hacen el código más comprensible y modificable

Clasificación de patrones de comportamiento

Patrón Propósito Complejidad
Observer Notificaciones de cambios Media
Strategy Algoritmos intercambiables Baja
Command Encapsular operaciones Media
State Cambio por estado Media
Template Method Esqueleto de algoritmo Baja
Iterator Recorrer colecciones Baja
Mediator Comunicación centralizada Alta
Memento Guardar/restaurar estado Media

Paso 2: Observer - Notificaciones de cambios

El patrón Observer define una dependencia de uno a muchos entre objetos, de modo que cuando un objeto cambia su estado, todos sus dependientes son notificados y actualizados automáticamente.

Implementación básica

#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>

// Interfaz abstracta para observadores
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const std::string& estado) = 0;
};

// Sujeto que mantiene una lista de observadores
class Subject {
private:
    std::vector<std::shared_ptr<Observer>> observers_;
    std::string estado_;

public:
    void attach(const std::shared_ptr<Observer>& observer) {
        observers_.push_back(observer);
    }

    void detach(const std::shared_ptr<Observer>& observer) {
        observers_.erase(
            std::remove(observers_.begin(), observers_.end(), observer),
            observers_.end()
        );
    }

    void notify() {
        for (const auto& observer : observers_) {
            observer->update(estado_);
        }
    }

    void setEstado(const std::string& nuevo_estado) {
        estado_ = nuevo_estado;
        notify();
    }

    const std::string& getEstado() const {
        return estado_;
    }
};

// Observadores concretos
class ConcreteObserverA : public Observer {
public:
    void update(const std::string& estado) override {
        std::cout << "Observador A: Estado cambiado a " << estado << std::endl;
    }
};

class ConcreteObserverB : public Observer {
public:
    void update(const std::string& estado) override {
        std::cout << "Observador B: Recibido cambio - " << estado << std::endl;
    }
};

// Uso
int main() {
    auto subject = std::make_shared<Subject>();

    auto observer_a = std::make_shared<ConcreteObserverA>();
    auto observer_b = std::make_shared<ConcreteObserverB>();

    subject->attach(observer_a);
    subject->attach(observer_b);

    subject->setEstado("Nuevo estado");
    subject->setEstado("Otro estado");

    subject->detach(observer_b);
    subject->setEstado("Estado sin observador B");

    return 0;
}

Observer para sistema de notificaciones

#include <iostream>
#include <vector>
#include <memory>
#include <string>

// Sistema de notificaciones que hereda de Subject
class NotificationSystem : public Subject {
private:
    std::vector<std::string> messages_;

public:
    void sendMessage(const std::string& message) {
        messages_.push_back(message);
        setEstado("Nuevo mensaje: " + message);
    }

    const std::vector<std::string>& getMessages() const {
        return messages_;
    }
};

// Cliente de email
class EmailClient : public Observer {
private:
    std::string email_;

public:
    EmailClient(const std::string& email) : email_(email) {}

    void update(const std::string& estado) override {
        // En una implementación real, aquí se enviaría el email
        std::cout << "Enviando email a " << email_ << ": " << estado << std::endl;
    }
};

// Cliente SMS
class SMSClient : public Observer {
private:
    std::string phone_;

public:
    SMSClient(const std::string& phone) : phone_(phone) {}

    void update(const std::string& estado) override {
        // En una implementación real, aquí se enviaría el SMS
        std::cout << "Enviando SMS a " << phone_ << ": " << estado << std::endl;
    }
};

// Uso
int main() {
    auto notification_system = std::make_shared<NotificationSystem>();

    auto email_client1 = std::make_shared<EmailClient>("[email protected]");
    auto sms_client = std::make_shared<SMSClient>("+123456789");
    auto email_client2 = std::make_shared<EmailClient>("[email protected]");

    notification_system->attach(email_client1);
    notification_system->attach(sms_client);
    notification_system->attach(email_client2);

    notification_system->sendMessage("Sistema iniciado");
    notification_system->sendMessage("Backup completado");
    notification_system->sendMessage("Error crítico detectado");

    return 0;
}

Paso 3: Strategy - Algoritmos intercambiables

El patrón Strategy define una familia de algoritmos, encapsula cada uno y los hace intercambiables. Permite que el algoritmo varíe independientemente de los clientes que lo usan.

Implementación básica

#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>

// Interfaz abstracta para estrategias de ordenamiento
template <typename T>
class SortStrategy {
public:
    virtual ~SortStrategy() = default;
    virtual std::vector<T> sort(const std::vector<T>& data) const = 0;
    virtual std::string getName() const = 0;
};

// Estrategia de ordenamiento burbuja
template <typename T>
class BubbleSort : public SortStrategy<T> {
public:
    std::vector<T> sort(const std::vector<T>& data) const override {
        std::vector<T> result = data;  // Copia para no modificar original
        size_t n = result.size();

        for (size_t i = 0; i < n - 1; ++i) {
            for (size_t j = 0; j < n - i - 1; ++j) {
                if (result[j] > result[j + 1]) {
                    std::swap(result[j], result[j + 1]);
                }
            }
        }

        return result;
    }

    std::string getName() const override {
        return "Bubble Sort";
    }
};

// Estrategia de ordenamiento rápido (quicksort)
template <typename T>
class QuickSort : public SortStrategy<T> {
private:
    std::vector<T> quickSortRecursive(std::vector<T> data) const {
        if (data.size() <= 1) {
            return data;
        }

        T pivot = data[data.size() / 2];
        std::vector<T> left, middle, right;

        for (const T& element : data) {
            if (element < pivot) {
                left.push_back(element);
            } else if (element == pivot) {
                middle.push_back(element);
            } else {
                right.push_back(element);
            }
        }

        std::vector<T> sorted_left = quickSortRecursive(left);
        std::vector<T> sorted_right = quickSortRecursive(right);

        sorted_left.insert(sorted_left.end(), middle.begin(), middle.end());
        sorted_left.insert(sorted_left.end(), sorted_right.begin(), sorted_right.end());

        return sorted_left;
    }

public:
    std::vector<T> sort(const std::vector<T>& data) const override {
        return quickSortRecursive(data);
    }

    std::string getName() const override {
        return "Quick Sort";
    }
};

// Estrategia de ordenamiento por selección
template <typename T>
class SelectionSort : public SortStrategy<T> {
public:
    std::vector<T> sort(const std::vector<T>& data) const override {
        std::vector<T> result = data;
        size_t n = result.size();

        for (size_t i = 0; i < n - 1; ++i) {
            size_t min_idx = i;
            for (size_t j = i + 1; j < n; ++j) {
                if (result[j] < result[min_idx]) {
                    min_idx = j;
                }
            }
            if (min_idx != i) {
                std::swap(result[i], result[min_idx]);
            }
        }

        return result;
    }

    std::string getName() const override {
        return "Selection Sort";
    }
};

// Contexto que usa la estrategia
template <typename T>
class SortContext {
private:
    std::unique_ptr<SortStrategy<T>> strategy_;

public:
    void setStrategy(std::unique_ptr<SortStrategy<T>> strategy) {
        strategy_ = std::move(strategy);
    }

    std::vector<T> executeSort(const std::vector<T>& data) const {
        if (!strategy_) {
            throw std::runtime_error("Estrategia no establecida");
        }

        std::cout << "Usando estrategia: " << strategy_->getName() << std::endl;
        return strategy_->sort(data);
    }
};

// Uso
int main() {
    std::vector<int> data = {64, 34, 25, 12, 22, 11, 90};
    SortContext<int> context;

    std::cout << "Datos originales: ";
    for (int num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // Usar burbuja
    context.setStrategy(std::make_unique<BubbleSort<int>>());
    auto result = context.executeSort(data);
    std::cout << "Burbuja: ";
    for (int num : result) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // Cambiar a quicksort
    context.setStrategy(std::make_unique<QuickSort<int>>());
    result = context.executeSort(data);
    std::cout << "Quicksort: ";
    for (int num : result) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // Cambiar a selección
    context.setStrategy(std::make_unique<SelectionSort<int>>());
    result = context.executeSort(data);
    std::cout << "Selección: ";
    for (int num : result) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

Strategy para procesamiento de pagos

#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <iomanip>

// Interfaz abstracta para estrategias de pago
class PaymentStrategy {
public:
    virtual ~PaymentStrategy() = default;
    virtual std::string processPayment(double amount) const = 0;
    virtual std::string getName() const = 0;
};

// Estrategia de pago con tarjeta de crédito
class CreditCardPayment : public PaymentStrategy {
public:
    std::string processPayment(double amount) const override {
        return "Procesando $" + std::to_string(amount) + " con tarjeta de crédito";
    }

    std::string getName() const override {
        return "Tarjeta de Crédito";
    }
};

// Estrategia de pago con PayPal
class PayPalPayment : public PaymentStrategy {
public:
    std::string processPayment(double amount) const override {
        return "Procesando $" + std::to_string(amount) + " con PayPal";
    }

    std::string getName() const override {
        return "PayPal";
    }
};

// Estrategia de pago con Bitcoin
class BitcoinPayment : public PaymentStrategy {
public:
    std::string processPayment(double amount) const override {
        return "Procesando $" + std::to_string(amount) + " con Bitcoin";
    }

    std::string getName() const override {
        return "Bitcoin";
    }
};

// Carrito de compras que usa la estrategia
class ShoppingCart {
private:
    std::vector<std::pair<std::string, double>> items_;
    std::unique_ptr<PaymentStrategy> payment_strategy_;

public:
    void addItem(const std::string& item, double price) {
        items_.emplace_back(item, price);
    }

    double calculateTotal() const {
        double total = 0.0;
        for (const auto& item : items_) {
            total += item.second;
        }
        return total;
    }

    void setPaymentMethod(std::unique_ptr<PaymentStrategy> strategy) {
        payment_strategy_ = std::move(strategy);
    }

    std::string checkout() const {
        if (!payment_strategy_) {
            throw std::runtime_error("Método de pago no seleccionado");
        }

        double total = calculateTotal();
        return payment_strategy_->processPayment(total);
    }

    void displayCart() const {
        std::cout << "Carrito de compras:" << std::endl;
        for (const auto& item : items_) {
            std::cout << "  - " << item.first << ": $" << std::fixed
                      << std::setprecision(2) << item.second << std::endl;
        }
        std::cout << "Total: $" << std::fixed << std::setprecision(2)
                  << calculateTotal() << std::endl;
    }
};

// Uso
int main() {
    ShoppingCart cart;

    cart.addItem("Laptop", 1200.00);
    cart.addItem("Mouse", 45.99);
    cart.addItem("Teclado", 89.99);

    cart.displayCart();

    // Procesar con diferentes métodos de pago
    cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
    std::cout << cart.checkout() << std::endl;

    cart.setPaymentMethod(std::make_unique<PayPalPayment>());
    std::cout << cart.checkout() << std::endl;

    cart.setPaymentMethod(std::make_unique<BitcoinPayment>());
    std::cout << cart.checkout() << std::endl;

    return 0;
}

Paso 4: Command - Encapsular operaciones

El patrón Command encapsula una solicitud como un objeto, permitiendo parametrizar clientes con diferentes solicitudes, encolar o registrar solicitudes, y soportar operaciones que pueden ser deshechas.

Implementación básica

#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <functional>

// Interfaz abstracta para comandos
class Command {
public:
    virtual ~Command() = default;
    virtual std::string execute() = 0;
    virtual std::string undo() = 0;
};

// Receptor que realiza las acciones
class Receiver {
private:
    std::string estado_;

public:
    Receiver() : estado_("Estado inicial") {}

    std::string specialAction(const std::string& message) {
        estado_ = "Estado después de: " + message;
        return "Acción ejecutada: " + message;
    }

    std::string anotherAction(int number) {
        estado_ = "Estado numérico: " + std::to_string(number);
        return "Número procesado: " + std::to_string(number);
    }

    const std::string& getEstado() const {
        return estado_;
    }

    void setEstado(const std::string& estado) {
        estado_ = estado;
    }
};

// Comando concreto
class ConcreteCommand : public Command {
private:
    std::shared_ptr<Receiver> receiver_;
    std::string action_;
    std::string message_param_;
    int int_param_;
    std::string estado_anterior_;
    bool has_message_param_;
    bool has_int_param_;

public:
    ConcreteCommand(std::shared_ptr<Receiver> receiver, const std::string& action)
        : receiver_(receiver), action_(action), has_message_param_(false), has_int_param_(false) {}

    ConcreteCommand(std::shared_ptr<Receiver> receiver, const std::string& action,
                   const std::string& message)
        : receiver_(receiver), action_(action), message_param_(message),
          has_message_param_(true), has_int_param_(false) {}

    ConcreteCommand(std::shared_ptr<Receiver> receiver, const std::string& action, int number)
        : receiver_(receiver), action_(action), int_param_(number),
          has_message_param_(false), has_int_param_(true) {}

    std::string execute() override {
        // Guardar estado anterior para poder deshacer
        estado_anterior_ = receiver_->getEstado();

        if (action_ == "specialAction") {
            return receiver_->specialAction(message_param_);
        } else if (action_ == "anotherAction") {
            return receiver_->anotherAction(int_param_);
        }

        return "Acción desconocida";
    }

    std::string undo() override {
        if (!estado_anterior_.empty()) {
            receiver_->setEstado(estado_anterior_);
            return "Operación deshecha";
        }
        return "No se puede deshacer";
    }
};

// Invocador que gestiona comandos
class Invoker {
private:
    std::vector<std::shared_ptr<Command>> history_;
    std::vector<std::shared_ptr<Command>> commands_to_execute_;

public:
    void setCommand(std::shared_ptr<Command> command) {
        commands_to_execute_.push_back(command);
    }

    std::vector<std::string> executeCommands() {
        std::vector<std::string> results;

        for (const auto& command : commands_to_execute_) {
            std::string result = command->execute();
            history_.push_back(command);
            results.push_back(result);
        }

        commands_to_execute_.clear();
        return results;
    }

    std::string undoLast() {
        if (!history_.empty()) {
            auto command = history_.back();
            history_.pop_back();
            return command->undo();
        }
        return "No hay comandos para deshacer";
    }
};

// Uso
int main() {
    auto receiver = std::make_shared<Receiver>();
    auto invoker = std::make_unique<Invoker>();

    // Configurar comandos
    auto command1 = std::make_shared<ConcreteCommand>(receiver, "specialAction", "Hola Mundo");
    auto command2 = std::make_shared<ConcreteCommand>(receiver, "anotherAction", 42);

    invoker->setCommand(command1);
    invoker->setCommand(command2);

    std::cout << "Estado inicial: " << receiver->getEstado() << std::endl;

    // Ejecutar comandos
    auto results = invoker->executeCommands();
    for (const auto& result : results) {
        std::cout << "Resultado: " << result << std::endl;
    }

    std::cout << "Estado después de ejecutar: " << receiver->getEstado() << std::endl;

    // Deshacer último comando
    std::cout << invoker->undoLast() << std::endl;
    std::cout << "Estado después de deshacer: " << receiver->getEstado() << std::endl;

    // Deshacer otro comando
    std::cout << invoker->undoLast() << std::endl;
    std::cout << "Estado final: " << receiver->getEstado() << std::endl;

    return 0;
}

Command para editor de texto

#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <sstream>

// Editor de texto
class TextEditor {
private:
    std::string texto_;
    size_t cursor_posicion_;

public:
    TextEditor() : texto_(""), cursor_posicion_(0) {}

    std::string insertarTexto(const std::string& texto, size_t posicion = std::string::npos) {
        if (posicion != std::string::npos) {
            cursor_posicion_ = posicion;
        }

        // Insertar texto en la posición del cursor
        texto_ = texto_.substr(0, cursor_posicion_) +
                texto +
                texto_.substr(cursor_posicion_);

        cursor_posicion_ += texto.length();
        return "Texto insertado: '" + texto + "'";
    }

    std::string borrarTexto(size_t cantidad = 1) {
        if (cursor_posicion_ >= cantidad) {
            size_t inicio = cursor_posicion_ - cantidad;
            size_t fin = cursor_posicion_;
            std::string texto_borrado = texto_.substr(inicio, cantidad);

            texto_ = texto_.substr(0, inicio) + texto_.substr(fin);
            cursor_posicion_ -= cantidad;

            return "Texto borrado: '" + texto_borrado + "'";
        }
        return "No hay texto para borrar";
    }

    struct Estado {
        std::string texto;
        size_t cursor;
    };

    Estado obtenerEstado() const {
        return {texto_, cursor_posicion_};
    }

    void establecerEstado(const Estado& estado) {
        texto_ = estado.texto;
        cursor_posicion_ = estado.cursor;
    }

    void mostrarEstado() const {
        std::string contenido_mostrado = texto_;
        if (cursor_posicion_ < contenido_mostrado.length()) {
            contenido_mostrado = contenido_mostrado.substr(0, cursor_posicion_) + "|" +
                               contenido_mostrado.substr(cursor_posicion_);
        } else {
            contenido_mostrado += "|";
        }
        std::cout << "Texto: '" << contenido_mostrado << "'" << std::endl;
    }
};

// Comando de inserción
class InsertCommand : public Command {
private:
    std::shared_ptr<TextEditor> editor_;
    std::string texto_;
    size_t posicion_;
    TextEditor::Estado estado_anterior_;

public:
    InsertCommand(std::shared_ptr<TextEditor> editor, const std::string& texto, size_t posicion = std::string::npos)
        : editor_(editor), texto_(texto), posicion_(posicion) {}

    std::string execute() override {
        estado_anterior_ = editor_->obtenerEstado();
        return editor_->insertarTexto(texto_, posicion_);
    }

    std::string undo() override {
        editor_->establecerEstado(estado_anterior_);
        return "Deshecha inserción de '" + texto_ + "'";
    }
};

// Comando de borrado
class DeleteCommand : public Command {
private:
    std::shared_ptr<TextEditor> editor_;
    size_t cantidad_;
    TextEditor::Estado estado_anterior_;
    std::string texto_borrado_;

public:
    DeleteCommand(std::shared_ptr<TextEditor> editor, size_t cantidad = 1)
        : editor_(editor), cantidad_(cantidad) {}

    std::string execute() override {
        estado_anterior_ = editor_->obtenerEstado();
        std::string resultado = editor_->borrarTexto(cantidad_);

        // Extraer texto borrado del resultado
        size_t inicio = resultado.find("'");
        size_t fin = resultado.find("'", inicio + 1);
        if (inicio != std::string::npos && fin != std::string::npos) {
            texto_borrado_ = resultado.substr(inicio + 1, fin - inicio - 1);
        }

        return resultado;
    }

    std::string undo() override {
        editor_->establecerEstado(estado_anterior_);
        return "Deshecho borrado de '" + texto_borrado_ + "'";
    }
};

// Uso del editor
int main() {
    auto editor = std::make_shared<TextEditor>();
    auto invoker = std::make_unique<Invoker>();

    std::cout << "=== Editor de Texto con Command Pattern ===" << std::endl;

    // Comandos de inserción
    invoker->setCommand(std::make_shared<InsertCommand>(editor, "Hola"));
    invoker->setCommand(std::make_shared<InsertCommand>(editor, " Mundo"));
    invoker->setCommand(std::make_shared<InsertCommand>(editor, "!", editor->obtenerEstado().texto.length()));

    // Ejecutar todos los comandos
    auto resultados = invoker->executeCommands();
    for (const auto& resultado : resultados) {
        std::cout << resultado << std::endl;
    }

    editor->mostrarEstado();

    // Comando de borrado
    invoker->setCommand(std::make_shared<DeleteCommand>(editor, 6));  // Borrar " Mundo"
    auto resultado_borrado = invoker->executeCommands()[0];
    std::cout << resultado_borrado << std::endl;
    editor->mostrarEstado();

    // Deshacer borrado
    std::cout << invoker->undoLast() << std::endl;
    editor->mostrarEstado();

    return 0;
}

Paso 5: State - Cambio de comportamiento por estado

El patrón State permite que un objeto altere su comportamiento cuando su estado interno cambia. El objeto parecerá haber cambiado su clase.

Implementación básica

#include <iostream>
#include <memory>

// Interfaz abstracta para estados
class State {
public:
    virtual ~State() = default;
    virtual void handle(Context& context) = 0;
    virtual std::string getName() const = 0;
};

// Contexto que mantiene el estado actual
class Context {
private:
    std::unique_ptr<State> current_state_;

public:
    explicit Context(std::unique_ptr<State> initial_state)
        : current_state_(std::move(initial_state)) {}

    void setState(std::unique_ptr<State> new_state) {
        std::string old_name = current_state_->getName();
        std::string new_name = new_state->getName();
        std::cout << "Cambiando estado de " << old_name << " a " << new_name << std::endl;
        current_state_ = std::move(new_state);
    }

    void request() {
        current_state_->handle(*this);
    }

    const std::string& getCurrentStateName() const {
        return current_state_->getName();
    }
};

// Estados concretos
class ConcreteStateA : public State {
public:
    void handle(Context& context) override {
        std::cout << "Manejando en Estado A" << std::endl;
        context.setState(std::make_unique<ConcreteStateB>());
    }

    std::string getName() const override {
        return "Estado A";
    }
};

class ConcreteStateB : public State {
public:
    void handle(Context& context) override {
        std::cout << "Manejando en Estado B" << std::endl;
        context.setState(std::make_unique<ConcreteStateC>());
    }

    std::string getName() const override {
        return "Estado B";
    }
};

class ConcreteStateC : public State {
public:
    void handle(Context& context) override {
        std::cout << "Manejando en Estado C" << std::endl;
        context.setState(std::make_unique<ConcreteStateA>());
    }

    std::string getName() const override {
        return "Estado C";
    }
};

// Uso
int main() {
    auto context = std::make_unique<Context>(std::make_unique<ConcreteStateA>());

    std::cout << "=== Patrón State ===" << std::endl;

    for (int i = 0; i < 6; ++i) {
        std::cout << "\nSolicitud " << (i + 1) << ":" << std::endl;
        context->request();
        std::cout << "Estado actual: " << context->getCurrentStateName() << std::endl;
    }

    return 0;
}

State para reproductor de música

#include <iostream>
#include <memory>
#include <string>

// Interfaz abstracta para estados del reproductor
class PlayerState {
public:
    virtual ~PlayerState() = default;
    virtual void play(MusicPlayer& player) = 0;
    virtual void pause(MusicPlayer& player) = 0;
    virtual void stop(MusicPlayer& player) = 0;
    virtual std::string getName() const = 0;
};

// Reproductor de música
class MusicPlayer {
private:
    std::unique_ptr<PlayerState> current_state_;
    std::string current_song_;

public:
    MusicPlayer() : current_song_("Canción de ejemplo") {
        current_state_ = std::make_unique<StoppedState>();
    }

    void setState(std::unique_ptr<PlayerState> new_state) {
        current_state_ = std::move(new_state);
    }

    void play() {
        current_state_->play(*this);
    }

    void pause() {
        current_state_->pause(*this);
    }

    void stop() {
        current_state_->stop(*this);
    }

    void showStatus() const {
        std::cout << "Estado actual: " << current_state_->getName()
                  << " | Canción: " << current_song_ << std::endl;
    }

    const std::string& getCurrentSong() const {
        return current_song_;
    }
};

// Estado detenido
class StoppedState : public PlayerState {
public:
    void play(MusicPlayer& player) override {
        std::cout << "▶️ Iniciando reproducción" << std::endl;
        player.setState(std::make_unique<PlayingState>());
    }

    void pause(MusicPlayer& player) override {
        std::cout << "⏸️ No se puede pausar, el reproductor está detenido" << std::endl;
    }

    void stop(MusicPlayer& player) override {
        std::cout << "⏹️ El reproductor ya está detenido" << std::endl;
    }

    std::string getName() const override {
        return "Detenido";
    }
};

// Estado reproduciendo
class PlayingState : public PlayerState {
public:
    void play(MusicPlayer& player) override {
        std::cout << "▶️ Ya se está reproduciendo" << std::endl;
    }

    void pause(MusicPlayer& player) override {
        std::cout << "⏸️ Pausando reproducción" << std::endl;
        player.setState(std::make_unique<PausedState>());
    }

    void stop(MusicPlayer& player) override {
        std::cout << "⏹️ Deteniendo reproducción" << std::endl;
        player.setState(std::make_unique<StoppedState>());
    }

    std::string getName() const override {
        return "Reproduciendo";
    }
};

// Estado pausado
class PausedState : public PlayerState {
public:
    void play(MusicPlayer& player) override {
        std::cout << "▶️ Reanudando reproducción" << std::endl;
        player.setState(std::make_unique<PlayingState>());
    }

    void pause(MusicPlayer& player) override {
        std::cout << "⏸️ Ya está pausado" << std::endl;
    }

    void stop(MusicPlayer& player) override {
        std::cout << "⏹️ Deteniendo desde pausa" << std::endl;
        player.setState(std::make_unique<StoppedState>());
    }

    std::string getName() const override {
        return "Pausado";
    }
};

// Uso
int main() {
    MusicPlayer player;

    std::cout << "=== Reproductor de Música con State Pattern ===" << std::endl;

    std::vector<std::string> actions = {"play", "pause", "play", "pause", "stop", "pause"};

    for (size_t i = 0; i < actions.size(); ++i) {
        std::cout << "\n--- " << actions[i] << " ---" << std::endl;

        if (actions[i] == "play") {
            player.play();
        } else if (actions[i] == "pause") {
            player.pause();
        } else if (actions[i] == "stop") {
            player.stop();
        }

        player.showStatus();
    }

    return 0;
}

Paso 6: Template Method - Esqueleto de algoritmo

El patrón Template Method define el esqueleto de un algoritmo en una operación, delegando algunos pasos a las subclases. Permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar su estructura.

Implementación básica

#include <iostream>
#include <memory>
#include <vector>

// Clase abstracta que define el template method
class DataProcessor {
public:
    virtual ~DataProcessor() = default;

    // Template Method - define el esqueleto del algoritmo
    void process() {
        loadData();
        transformData();
        validateData();
        saveData();
        cleanup();
    }

    // Pasos abstractos que deben implementar las subclases
    virtual void loadData() = 0;
    virtual void transformData() = 0;
    virtual void saveData() = 0;

    // Pasos con implementación por defecto (hooks)
    virtual void validateData() {
        std::cout << "Validando datos (comportamiento por defecto)" << std::endl;
    }

    virtual void cleanup() {
        std::cout << "Limpiando recursos (comportamiento por defecto)" << std::endl;
    }
};

// Procesador para archivos CSV
class CSVProcessor : public DataProcessor {
public:
    void loadData() override {
        std::cout << "Cargando datos desde archivo CSV" << std::endl;
    }

    void transformData() override {
        std::cout << "Transformando datos CSV: parseando, limpiando formato" << std::endl;
    }

    void saveData() override {
        std::cout << "Guardando datos procesados en base de datos" << std::endl;
    }
};

// Procesador para archivos JSON
class JSONProcessor : public DataProcessor {
public:
    void loadData() override {
        std::cout << "Cargando datos desde archivo JSON" << std::endl;
    }

    void transformData() override {
        std::cout << "Transformando datos JSON: validando schema, convirtiendo tipos" << std::endl;
    }

    void validateData() override {
        std::cout << "Validación especial para JSON: verificando estructura" << std::endl;
    }

    void saveData() override {
        std::cout << "Guardando datos procesados en API REST" << std::endl;
    }
};

// Procesador para archivos XML
class XMLProcessor : public DataProcessor {
public:
    void loadData() override {
        std::cout << "Cargando datos desde archivo XML" << std::endl;
    }

    void transformData() override {
        std::cout << "Transformando datos XML: parseando, mapeando elementos" << std::endl;
    }

    void saveData() override {
        std::cout << "Guardando datos procesados en sistema de archivos" << std::endl;
    }

    void cleanup() override {
        std::cout << "Limpieza especial para XML: cerrando parsers" << std::endl;
    }
};

// Uso
int main() {
    std::cout << "=== Template Method Pattern ===" << std::endl;

    std::cout << "\n=== Procesando CSV ===" << std::endl;
    CSVProcessor csv_processor;
    csv_processor.process();

    std::cout << "\n=== Procesando JSON ===" << std::endl;
    JSONProcessor json_processor;
    json_processor.process();

    std::cout << "\n=== Procesando XML ===" << std::endl;
    XMLProcessor xml_processor;
    xml_processor.process();

    return 0;
}

Template Method para reportes

#include <iostream>
#include <vector>
#include <string>
#include <sstream>

// Clase abstracta que define el template method para generar reportes
class ReportGenerator {
public:
    virtual ~ReportGenerator() = default;

    // Template Method - define el esqueleto del algoritmo
    std::string generateReport() {
        prepareData();
        formatHeader();
        formatBody();
        formatFooter();
        finalizeReport();

        return getReport();
    }

    // Pasos comunes con implementación por defecto
    virtual void prepareData() {
        std::cout << "Preparando datos para el reporte" << std::endl;
    }

    virtual void formatFooter() {
        std::cout << "Agregando pie de página estándar" << std::endl;
    }

    virtual void finalizeReport() {
        std::cout << "Finalizando reporte" << std::endl;
    }

    // Pasos abstractos que deben implementar las subclases
    virtual void formatHeader() = 0;
    virtual void formatBody() = 0;
    virtual std::string getReport() = 0;
};

// Generador de reportes HTML
class HTMLReport : public ReportGenerator {
private:
    std::vector<std::string> content_;

public:
    void formatHeader() override {
        content_.push_back("<html><head><title>Reporte</title></head><body>");
        content_.push_back("<h1>Reporte HTML</h1>");
    }

    void formatBody() override {
        content_.push_back("<div class='contenido'>Contenido del reporte en HTML</div>");
    }

    void formatFooter() override {
        content_.push_back("<footer>Reporte generado automáticamente</footer>");
        content_.push_back("</body></html>");
    }

    std::string getReport() override {
        std::ostringstream oss;
        for (const auto& line : content_) {
            oss << line << "\n";
        }
        return oss.str();
    }
};

// Generador de reportes PDF
class PDFReport : public ReportGenerator {
private:
    std::vector<std::string> content_;

public:
    void formatHeader() override {
        content_.push_back("=== REPORTE PDF ===");
        content_.push_back("Título: Reporte en PDF");
    }

    void formatBody() override {
        content_.push_back("Contenido del reporte en formato PDF");
    }

    std::string getReport() override {
        std::ostringstream oss;
        for (const auto& line : content_) {
            oss << line << "\n";
        }
        return oss.str();
    }
};

// Generador de reportes CSV
class CSVReport : public ReportGenerator {
private:
    std::vector<std::string> content_;

public:
    void formatHeader() override {
        content_.push_back("columna1,columna2,columna3");
    }

    void formatBody() override {
        content_.push_back("dato1,dato2,dato3");
        content_.push_back("dato4,dato5,dato6");
    }

    // CSV no tiene pie de página tradicional
    void formatFooter() override {
        // No hacer nada para CSV
    }

    std::string getReport() override {
        std::ostringstream oss;
        for (const auto& line : content_) {
            oss << line << "\n";
        }
        return oss.str();
    }
};

// Uso
int main() {
    std::cout << "=== Template Method para Reportes ===" << std::endl;

    std::cout << "\n=== Generando Reporte HTML ===" << std::endl;
    HTMLReport html_report;
    std::string html = html_report.generateReport();
    std::cout << html;

    std::cout << "\n=== Generando Reporte PDF ===" << std::endl;
    PDFReport pdf_report;
    std::string pdf = pdf_report.generateReport();
    std::cout << pdf;

    std::cout << "\n=== Generando Reporte CSV ===" << std::endl;
    CSVReport csv_report;
    std::string csv = csv_report.generateReport();
    std::cout << csv;

    return 0;
}

Paso 7: Iterator - Recorrer colecciones

El patrón Iterator proporciona una forma de acceder secuencialmente a los elementos de un objeto agregado sin exponer su representación subyacente.

Implementación básica

#include <iostream>
#include <vector>
#include <memory>
#include <functional>

// Interfaz abstracta para iteradores
template <typename T>
class Iterator {
public:
    virtual ~Iterator() = default;
    virtual void first() = 0;
    virtual void next() = 0;
    virtual bool isDone() const = 0;
    virtual T currentItem() const = 0;
};

// Interfaz abstracta para agregados
template <typename T>
class Aggregate {
public:
    virtual ~Aggregate() = default;
    virtual std::unique_ptr<Iterator<T>> createIterator() const = 0;
};

// Iterador concreto para listas
template <typename T>
class ListIterator : public Iterator<T> {
private:
    const std::vector<T>& aggregate_;
    size_t current_position_;

public:
    explicit ListIterator(const std::vector<T>& aggregate)
        : aggregate_(aggregate), current_position_(0) {}

    void first() override {
        current_position_ = 0;
    }

    void next() override {
        if (current_position_ < aggregate_.size()) {
            ++current_position_;
        }
    }

    bool isDone() const override {
        return current_position_ >= aggregate_.size();
    }

    T currentItem() const override {
        if (!isDone()) {
            return aggregate_[current_position_];
        }
        throw std::out_of_range("Iterator is done");
    }
};

// Agregado concreto - Lista
template <typename T>
class ConcreteList : public Aggregate<T> {
private:
    std::vector<T> data_;

public:
    void add(const T& item) {
        data_.push_back(item);
    }

    const std::vector<T>& getData() const {
        return data_;
    }

    std::unique_ptr<Iterator<T>> createIterator() const override {
        return std::make_unique<ListIterator<T>>(data_);
    }
};

// Iterador con filtro
template <typename T>
class FilterIterator : public Iterator<T> {
private:
    std::vector<T> filtered_data_;
    size_t current_position_;

public:
    FilterIterator(const std::vector<T>& data, std::function<bool(const T&)> filter_func)
        : current_position_(0) {
        for (const auto& item : data) {
            if (filter_func(item)) {
                filtered_data_.push_back(item);
            }
        }
    }

    void first() override {
        current_position_ = 0;
    }

    void next() override {
        if (current_position_ < filtered_data_.size()) {
            ++current_position_;
        }
    }

    bool isDone() const override {
        return current_position_ >= filtered_data_.size();
    }

    T currentItem() const override {
        if (!isDone()) {
            return filtered_data_[current_position_];
        }
        throw std::out_of_range("Iterator is done");
    }
};

// Uso
int main() {
    std::cout << "=== Iterator Pattern ===" << std::endl;

    // Crear lista y agregar elementos
    ConcreteList<int> lista;
    lista.add(1);
    lista.add(2);
    lista.add(3);
    lista.add(4);
    lista.add(5);

    // Crear iterador y recorrer la lista
    auto iterador = lista.createIterator();

    std::cout << "Recorriendo lista:" << std::endl;
    for (iterador->first(); !iterador->isDone(); iterador->next()) {
        std::cout << "Elemento actual: " << iterador->currentItem() << std::endl;
    }

    // Iterador con filtro para números pares
    std::cout << "\nNúmeros pares:" << std::endl;
    FilterIterator<int> filter_iterador(lista.getData(),
        [](const int& num) { return num % 2 == 0; });

    for (filter_iterador.first(); !filter_iterador.isDone(); filter_iterador.next()) {
        std::cout << "Par: " << filter_iterador.currentItem() << std::endl;
    }

    return 0;
}

Iterator para árbol binario

#include <iostream>
#include <vector>
#include <memory>
#include <stack>

// Nodo del árbol binario
struct TreeNode {
    int valor;
    std::unique_ptr<TreeNode> izquierda;
    std::unique_ptr<TreeNode> derecha;

    TreeNode(int val) : valor(val), izquierda(nullptr), derecha(nullptr) {}
};

// Árbol binario
class BinaryTree {
private:
    std::unique_ptr<TreeNode> raiz_;

    void insertRecursive(std::unique_ptr<TreeNode>& nodo, int valor) {
        if (!nodo) {
            nodo = std::make_unique<TreeNode>(valor);
        } else if (valor < nodo->valor) {
            insertRecursive(nodo->izquierda, valor);
        } else {
            insertRecursive(nodo->derecha, valor);
        }
    }

public:
    BinaryTree() : raiz_(nullptr) {}

    void insertar(int valor) {
        insertRecursive(raiz_, valor);
    }

    const std::unique_ptr<TreeNode>& getRaiz() const {
        return raiz_;
    }
};

// Interfaz abstracta para iteradores de árbol
class TreeIterator {
public:
    virtual ~TreeIterator() = default;
    virtual void first() = 0;
    virtual void next() = 0;
    virtual bool isDone() const = 0;
    virtual int currentItem() const = 0;
};

// Iterador Inorden (izquierda, raíz, derecha)
class InorderIterator : public TreeIterator {
private:
    const TreeNode* raiz_;
    std::stack<const TreeNode*> pila_;

    void goLeft(const TreeNode* nodo) {
        while (nodo) {
            pila_.push(nodo);
            nodo = nodo->izquierda.get();
        }
    }

public:
    explicit InorderIterator(const TreeNode* raiz) : raiz_(raiz) {
        first();
    }

    void first() override {
        // Limpiar pila
        while (!pila_.empty()) {
            pila_.pop();
        }
        goLeft(raiz_);
    }

    void next() override {
        if (!pila_.empty()) {
            const TreeNode* nodo = pila_.top();
            pila_.pop();

            // Si hay subárbol derecho, recorrerlo
            if (nodo->derecha) {
                goLeft(nodo->derecha.get());
            }
        }
    }

    bool isDone() const override {
        return pila_.empty();
    }

    int currentItem() const override {
        if (!isDone()) {
            return pila_.top()->valor;
        }
        throw std::out_of_range("Iterator is done");
    }
};

// Iterador Preorden (raíz, izquierda, derecha)
class PreorderIterator : public TreeIterator {
private:
    const TreeNode* raiz_;
    std::stack<const TreeNode*> pila_;

public:
    explicit PreorderIterator(const TreeNode* raiz) : raiz_(raiz) {
        first();
    }

    void first() override {
        // Limpiar pila
        while (!pila_.empty()) {
            pila_.pop();
        }
        if (raiz_) {
            pila_.push(raiz_);
        }
    }

    void next() override {
        if (!pila_.empty()) {
            const TreeNode* nodo = pila_.top();
            pila_.pop();

            // Agregar primero derecha, luego izquierda (para que izquierda se procese primero)
            if (nodo->derecha) {
                pila_.push(nodo->derecha.get());
            }
            if (nodo->izquierda) {
                pila_.push(nodo->izquierda.get());
            }
        }
    }

    bool isDone() const override {
        return pila_.empty();
    }

    int currentItem() const override {
        if (!isDone()) {
            return pila_.top()->valor;
        }
        throw std::out_of_range("Iterator is done");
    }
};

// Iterador Postorden (izquierda, derecha, raíz)
class PostorderIterator : public TreeIterator {
private:
    const TreeNode* raiz_;
    std::stack<const TreeNode*> pila_;

    void preparePostorder(const TreeNode* nodo) {
        if (nodo) {
            pila_.push(nodo);
            if (nodo->derecha) {
                preparePostorder(nodo->derecha.get());
            }
            if (nodo->izquierda) {
                preparePostorder(nodo->izquierda.get());
            }
        }
    }

public:
    explicit PostorderIterator(const TreeNode* raiz) : raiz_(raiz) {
        first();
    }

    void first() override {
        // Limpiar pila
        while (!pila_.empty()) {
            pila_.pop();
        }
        preparePostorder(raiz_);
    }

    void next() override {
        if (!pila_.empty()) {
            pila_.pop();
        }
    }

    bool isDone() const override {
        return pila_.empty();
    }

    int currentItem() const override {
        if (!isDone()) {
            return pila_.top()->valor;
        }
        throw std::out_of_range("Iterator is done");
    }
};

// Uso del árbol con diferentes iteradores
int main() {
    std::cout << "=== Iterator Pattern para Árbol Binario ===" << std::endl;

    BinaryTree arbol;
    std::vector<int> valores = {5, 3, 7, 2, 4, 6, 8};

    for (int valor : valores) {
        arbol.insertar(valor);
    }

    std::cout << "=== Recorrido Inorden (ordenado) ===" << std::endl;
    InorderIterator inorden(arbol.getRaiz().get());
    for (inorden.first(); !inorden.isDone(); inorden.next()) {
        std::cout << "Valor: " << inorden.currentItem() << std::endl;
    }

    std::cout << "\n=== Recorrido Preorden ===" << std::endl;
    PreorderIterator preorden(arbol.getRaiz().get());
    for (preorden.first(); !preorden.isDone(); preorden.next()) {
        std::cout << "Valor: " << preorden.currentItem() << std::endl;
    }

    std::cout << "\n=== Recorrido Postorden ===" << std::endl;
    PostorderIterator postorden(arbol.getRaiz().get());
    for (postorden.first(); !postorden.isDone(); postorden.next()) {
        std::cout << "Valor: " << postorden.currentItem() << std::endl;
    }

    return 0;
}

Paso 8: Mediator - Comunicación centralizada

El patrón Mediator define un objeto que encapsula cómo un conjunto de objetos interactúa. Promueve el acoplamiento débil al evitar que los objetos se refieran entre sí explícitamente, lo cual es especialmente útil en C++ para mantener la encapsulación y facilitar el testing.

Implementación básica

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <algorithm>

// Interfaz abstracta para colegas
class Colega;

class Mediador {
public:
    virtual ~Mediador() = default;
    virtual void registrarColega(std::shared_ptr<Colega> colega) = 0;
    virtual void enviarMensaje(const std::string& mensaje,
                              std::shared_ptr<Colega> remitente,
                              const std::string& destinatario = "") = 0;
};

class Colega : public std::enable_shared_from_this<Colega> {
protected:
    std::string nombre_;
    std::shared_ptr<Mediador> mediador_;

public:
    Colega(const std::string& nombre, std::shared_ptr<Mediador> mediador)
        : nombre_(nombre), mediador_(mediador) {}

    virtual ~Colega() = default;

    const std::string& getNombre() const { return nombre_; }

    virtual void enviar(const std::string& mensaje, const std::string& destinatario = "") = 0;
    virtual void recibir(const std::string& mensaje, const std::string& remitente) = 0;
};

// Mediador concreto
class MediadorConcreto : public Mediador {
private:
    std::unordered_map<std::string, std::shared_ptr<Colega>> colegas_;

public:
    void registrarColega(std::shared_ptr<Colega> colega) override {
        colegas_[colega->getNombre()] = colega;
    }

    void enviarMensaje(const std::string& mensaje,
                      std::shared_ptr<Colega> remitente,
                      const std::string& destinatario = "") override {
        if (!destinatario.empty()) {
            // Mensaje privado
            auto it = colegas_.find(destinatario);
            if (it != colegas_.end()) {
                it->second->recibir(mensaje, remitente->getNombre());
            } else {
                std::cout << "❌ Destinatario '" << destinatario << "' no encontrado" << std::endl;
            }
        } else {
            // Broadcast a todos excepto remitente
            for (const auto& par : colegas_) {
                if (par.first != remitente->getNombre()) {
                    par.second->recibir(mensaje, remitente->getNombre());
                }
            }
        }
    }
};

// Colega concreto
class ColegaConcreto : public Colega {
public:
    ColegaConcreto(const std::string& nombre, std::shared_ptr<Mediador> mediador)
        : Colega(nombre, mediador) {}

    void enviar(const std::string& mensaje, const std::string& destinatario = "") override {
        std::cout << "📤 " << nombre_ << " enviando mensaje: '" << mensaje << "'" << std::endl;
        mediador_->enviarMensaje(mensaje, shared_from_this(), destinatario);
    }

    void recibir(const std::string& mensaje, const std::string& remitente) override {
        std::cout << "📥 " << nombre_ << " recibió de " << remitente << ": '" << mensaje << "'" << std::endl;
    }
};

// Uso del patrón Mediator
int main() {
    std::cout << "=== Mediator Pattern - Comunicación Centralizada ===" << std::endl;

    auto mediador = std::make_shared<MediadorConcreto>();

    // Crear colegas
    auto colega1 = std::make_shared<ColegaConcreto>("Usuario1", mediador);
    auto colega2 = std::make_shared<ColegaConcreto>("Usuario2", mediador);
    auto colega3 = std::make_shared<ColegaConcreto>("Usuario3", mediador);
    auto colega4 = std::make_shared<ColegaConcreto>("Usuario4", mediador);

    // Registrar colegas
    mediador->registrarColega(colega1);
    mediador->registrarColega(colega2);
    mediador->registrarColega(colega3);
    mediador->registrarColega(colega4);

    std::cout << "\n=== Comunicación con Mediador ===" << std::endl;

    // Mensajes broadcast
    colega1->enviar("¡Hola a todos!");
    colega2->enviar("¡Hola! ¿Cómo están?");

    // Mensajes privados
    colega3->enviar("Mensaje secreto para Usuario1", "Usuario1");
    colega1->enviar("Respuesta privada", "Usuario3");

    // Usuario no existente
    colega2->enviar("Mensaje para usuario inexistente", "Usuario99");

    return 0;
}

Mediator para sistema de chat

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>

// Usuario de chat
class UsuarioChat;

class MediadorChat;

class UsuarioChat : public Colega {
private:
    std::string sala_actual_;

public:
    UsuarioChat(const std::string& nombre, std::shared_ptr<MediadorChat> mediador, const std::string& sala_inicial);

    void enviar(const std::string& mensaje, const std::string& destinatario = "") override;
    void recibir(const std::string& mensaje, const std::string& remitente) override;

    void unirseSala(const std::string& sala);
    void salirSala();

    const std::string& getSalaActual() const { return sala_actual_; }
};

class MediadorChat : public MediadorConcreto {
private:
    std::unordered_map<std::string, std::vector<std::shared_ptr<UsuarioChat>>> salas_;

public:
    void registrarColega(std::shared_ptr<Colega> colega) override;
    void enviarMensaje(const std::string& mensaje,
                      std::shared_ptr<Colega> remitente,
                      const std::string& destinatario = "") override;

    void actualizarSala(std::shared_ptr<UsuarioChat> usuario, const std::string& nueva_sala);

private:
    void agregarASala(std::shared_ptr<UsuarioChat> usuario, const std::string& sala);
    void removerDeSala(std::shared_ptr<UsuarioChat> usuario, const std::string& sala);
};

// Implementación de UsuarioChat
UsuarioChat::UsuarioChat(const std::string& nombre, std::shared_ptr<MediadorChat> mediador, const std::string& sala_inicial)
    : Colega(nombre, mediador), sala_actual_(sala_inicial) {}

void UsuarioChat::enviar(const std::string& mensaje, const std::string& destinatario) {
    std::cout << "💬 " << nombre_ << " en " << sala_actual_ << ": '" << mensaje << "'" << std::endl;
    auto mediador_chat = std::static_pointer_cast<MediadorChat>(mediador_);
    mediador_chat->enviarMensaje(mensaje, shared_from_this(), destinatario);
}

void UsuarioChat::recibir(const std::string& mensaje, const std::string& remitente) {
    std::cout << "💭 " << nombre_ << " recibió de " << remitente << ": '" << mensaje << "'" << std::endl;
}

void UsuarioChat::unirseSala(const std::string& sala) {
    std::cout << "🚪 " << nombre_ << " se unió a " << sala << std::endl;
    auto mediador_chat = std::static_pointer_cast<MediadorChat>(mediador_);
    mediador_chat->actualizarSala(std::static_pointer_cast<UsuarioChat>(shared_from_this()), sala);
    sala_actual_ = sala;
}

void UsuarioChat::salirSala() {
    std::cout << "🚪 " << nombre_ << " salió de " << sala_actual_ << std::endl;
    auto mediador_chat = std::static_pointer_cast<MediadorChat>(mediador_);
    mediador_chat->actualizarSala(std::static_pointer_cast<UsuarioChat>(shared_from_this()), "");
    sala_actual_ = "";
}

// Implementación de MediadorChat
void MediadorChat::registrarColega(std::shared_ptr<Colega> colega) {
    MediadorConcreto::registrarColega(colega);
    auto usuario = std::static_pointer_cast<UsuarioChat>(colega);
    agregarASala(usuario, usuario->getSalaActual());
}

void MediadorChat::enviarMensaje(const std::string& mensaje,
                                std::shared_ptr<Colega> remitente,
                                const std::string& destinatario) {
    if (!destinatario.empty()) {
        // Mensaje privado
        MediadorConcreto::enviarMensaje(mensaje, remitente, destinatario);
    } else {
        // Mensaje a toda la sala
        auto usuario = std::static_pointer_cast<UsuarioChat>(remitente);
        auto sala = usuario->getSalaActual();

        auto it = salas_.find(sala);
        if (it != salas_.end()) {
            for (auto& colega : it->second) {
                if (colega->getNombre() != usuario->getNombre()) {
                    colega->recibir(mensaje, usuario->getNombre());
                }
            }
        }
    }
}

void MediadorChat::actualizarSala(std::shared_ptr<UsuarioChat> usuario, const std::string& nueva_sala) {
    // Remover de sala actual
    if (!usuario->getSalaActual().empty()) {
        removerDeSala(usuario, usuario->getSalaActual());
    }

    // Agregar a nueva sala
    if (!nueva_sala.empty()) {
        agregarASala(usuario, nueva_sala);
    }
}

void MediadorChat::agregarASala(std::shared_ptr<UsuarioChat> usuario, const std::string& sala) {
    salas_[sala].push_back(usuario);
}

void MediadorChat::removerDeSala(std::shared_ptr<UsuarioChat> usuario, const std::string& sala) {
    auto it = salas_.find(sala);
    if (it != salas_.end()) {
        auto& usuarios = it->second;
        usuarios.erase(std::remove_if(usuarios.begin(), usuarios.end(),
            [&usuario](const std::shared_ptr<UsuarioChat>& u) {
                return u->getNombre() == usuario->getNombre();
            }), usuarios.end());
    }
}

// Uso del sistema de chat
int main() {
    std::cout << "=== Sistema de Chat con Salas ===" << std::endl;

    auto mediador_chat = std::make_shared<MediadorChat>();

    // Crear usuarios
    auto usuario1 = std::make_shared<UsuarioChat>("Ana", mediador_chat, "general");
    auto usuario2 = std::make_shared<UsuarioChat>("Carlos", mediador_chat, "general");
    auto usuario3 = std::make_shared<UsuarioChat>("Maria", mediador_chat, "tecnologia");
    auto usuario4 = std::make_shared<UsuarioChat>("Pedro", mediador_chat, "tecnologia");

    // Registrar usuarios
    mediador_chat->registrarColega(usuario1);
    mediador_chat->registrarColega(usuario2);
    mediador_chat->registrarColega(usuario3);
    mediador_chat->registrarColega(usuario4);

    // Mensajes en salas
    usuario1->enviar("¡Hola a todos en general!");
    usuario3->enviar("Discutiendo sobre Python en tecnología");

    // Cambio de sala
    usuario2->unirseSala("tecnologia");
    usuario2->enviar("¡Hola! Me uní a tecnología");

    // Mensaje privado
    usuario1->enviar("Mensaje privado para Maria", "Maria");

    // Mostrar estado de salas
    std::cout << "\n=== Estado de Salas ===" << std::endl;
    for (const auto& par : mediador_chat->salas_) {
        std::cout << "Sala '" << par.first << "': ";
        for (size_t i = 0; i < par.second.size(); ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << par.second[i]->getNombre();
        }
        std::cout << std::endl;
    }

    return 0;
}

Paso 9: Memento - Guardar y restaurar estado

El patrón Memento captura y externaliza el estado interno de un objeto sin violar la encapsulación, para que el objeto pueda ser restaurado a este estado más tarde. En C++, este patrón es especialmente útil para implementar funcionalidades de "undo" y "redo" de manera segura y eficiente.

Implementación básica

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <stack>

// Memento: almacena el estado del originador
class Memento {
private:
    std::string estado_;
    friend class Originador;  // Permite acceso directo al estado

public:
    explicit Memento(const std::string& estado) : estado_(estado) {}

    const std::string& getEstado() const {
        return estado_;
    }

private:
    // Constructor privado para que solo Originador pueda crear mementos
    Memento() = default;
};

// Originador: crea y usa mementos para guardar/restaurar su estado
class Originador {
private:
    std::string estado_;

public:
    void establecerEstado(const std::string& estado) {
        std::cout << "Estableciendo estado: " << estado << std::endl;
        estado_ = estado;
    }

    const std::string& obtenerEstado() const {
        return estado_;
    }

    // Crear memento con el estado actual
    std::unique_ptr<Memento> guardarEnMemento() const {
        std::cout << "Guardando estado en memento: " << estado_ << std::endl;
        return std::make_unique<Memento>(estado_);
    }

    // Restaurar estado desde memento
    void restaurarDesdeMemento(const Memento& memento) {
        estado_ = memento.estado_;
        std::cout << "Estado restaurado desde memento: " << estado_ << std::endl;
    }
};

// Cuidador: gestiona los mementos (historial de estados)
class Cuidador {
private:
    Originador& originador_;
    std::stack<std::unique_ptr<Memento>> historial_;

public:
    explicit Cuidador(Originador& originador) : originador_(originador) {}

    // Guardar estado actual en el historial
    void guardar() {
        auto memento = originador_.guardarEnMemento();
        historial_.push(std::move(memento));
        std::cout << "Memento guardado. Total: " << historial_.size() << std::endl;
    }

    // Deshacer último cambio
    bool deshacer() {
        if (!historial_.empty()) {
            auto memento = std::move(historial_.top());
            historial_.pop();
            originador_.restaurarDesdeMemento(*memento);
            return true;
        }
        return false;
    }

    // Obtener número de estados guardados
    size_t getTamanoHistorial() const {
        return historial_.size();
    }
};

// Uso del patrón Memento
int main() {
    std::cout << "=== Memento Pattern - Guardar y Restaurar Estado ===" << std::endl;

    Originador originador;
    Cuidador cuidador(originador);

    // Crear y guardar estados
    originador.establecerEstado("Estado 1");
    cuidador.guardar();

    originador.establecerEstado("Estado 2");
    cuidador.guardar();

    originador.establecerEstado("Estado 3");
    cuidador.guardar();

    std::cout << "Estado actual: " << originador.obtenerEstado() << std::endl;

    // Deshacer cambios
    cuidador.deshacer();
    cuidador.deshacer();

    std::cout << "Estado después de deshacer 2 veces: " << originador.obtenerEstado() << std::endl;

    return 0;
}

Memento para editor de texto

#include <iostream>
#include <string>
#include <memory>
#include <stack>
#include <utility>

// Estado del documento
struct DocumentState {
    std::string contenido;
    size_t cursor_posicion;

    DocumentState(const std::string& cont, size_t cursor)
        : contenido(cont), cursor_posicion(cursor) {}
};

// Editor de texto
class TextEditor {
private:
    std::string contenido_;
    size_t cursor_posicion_;

public:
    TextEditor() : contenido_(""), cursor_posicion_(0) {}

    void escribirTexto(const std::string& texto) {
        // Insertar texto en la posición del cursor
        contenido_ = contenido_.substr(0, cursor_posicion_) +
                    texto +
                    contenido_.substr(cursor_posicion_);

        cursor_posicion_ += texto.length();
        std::cout << "📝 Escrito: '" << texto << "'" << std::endl;
    }

    void borrarCaracter(size_t cantidad = 1) {
        if (cursor_posicion_ >= cantidad) {
            size_t inicio = cursor_posicion_ - cantidad;
            size_t fin = cursor_posicion_;
            std::string texto_borrado = contenido_.substr(inicio, cantidad);

            contenido_ = contenido_.substr(0, inicio) + contenido_.substr(fin);
            cursor_posicion_ -= cantidad;

            std::cout << "⌫ Borrado: '" << texto_borrado << "'" << std::endl;
        } else {
            std::cout << "No hay texto para borrar" << std::endl;
        }
    }

    void moverCursor(size_t posicion) {
        if (posicion <= contenido_.length()) {
            cursor_posicion_ = posicion;
            std::cout << "📍 Cursor movido a posición " << posicion << std::endl;
        }
    }

    DocumentState obtenerEstado() const {
        return DocumentState(contenido_, cursor_posicion_);
    }

    void establecerEstado(const DocumentState& estado) {
        contenido_ = estado.contenido;
        cursor_posicion_ = estado.cursor_posicion;
    }

    void mostrarEstado() const {
        std::string contenido_mostrado = contenido_;
        if (cursor_posicion_ < contenido_mostrado.length()) {
            contenido_mostrado = contenido_mostrado.substr(0, cursor_posicion_) + "|" +
                               contenido_mostrado.substr(cursor_posicion_);
        } else {
            contenido_mostrado += "|";
        }
        std::cout << "📄 Contenido: '" << contenido_mostrado << "'" << std::endl;
    }

    const std::string& getContenido() const { return contenido_; }
    size_t getCursorPosicion() const { return cursor_posicion_; }
};

// Historial del editor usando Memento
class EditorHistory {
private:
    TextEditor& editor_;
    std::stack<DocumentState> historial_;

public:
    explicit EditorHistory(TextEditor& editor) : editor_(editor) {}

    void guardarEstado() {
        historial_.push(editor_.obtenerEstado());
        std::cout << "💾 Estado guardado (total: " << historial_.size() << ")" << std::endl;
    }

    bool deshacer() {
        if (!historial_.empty()) {
            DocumentState estado = historial_.top();
            historial_.pop();
            editor_.establecerEstado(estado);
            std::cout << "🔄 Deshecho último cambio" << std::endl;
            return true;
        }
        std::cout << "❌ No hay cambios para deshacer" << std::endl;
        return false;
    }

    size_t getTamanoHistorial() const {
        return historial_.size();
    }
};

// Uso del editor con Memento
int main() {
    std::cout << "=== Memento para Editor de Texto ===" << std::endl;

    TextEditor editor;
    EditorHistory historial(editor);

    editor.mostrarEstado();

    editor.escribirTexto("Hola");
    historial.guardarEstado();
    editor.mostrarEstado();

    editor.escribirTexto(" mundo");
    historial.guardarEstado();
    editor.mostrarEstado();

    editor.borrarCaracter(6);  // Borrar " mundo"
    editor.mostrarEstado();

    std::cout << "\n=== Deshaciendo ===" << std::endl;
    historial.deshacer();
    editor.mostrarEstado();

    historial.deshacer();
    editor.mostrarEstado();

    return 0;
}

Paso 10: Comparación y selección de patrones

Cuándo usar cada patrón de comportamiento en C++

Patrón Cuándo usarlo Ventajas en C++ Desventajas en C++
Observer Notificaciones, eventos, sistemas reactivos Type safety, RAII, thread-safe con mutex Overhead de virtual functions, gestión de lifetime
Strategy Algoritmos variables, políticas configurables Templates para type safety, inlining, zero-cost abstractions Code bloat con templates, complejidad de compilación
Command Operaciones reversibles, colas, transacciones RAII para cleanup, move semantics, smart pointers Overhead de indirección, memoria extra para commands
State Estados complejos, máquinas de estado Performance óptima, inlining de estados, type safety Code duplication, difícil de extender
Template Method Algoritmos con pasos variables, frameworks Non-virtual interface idiom, CRTP, compile-time polymorphism Tight coupling, difícil de mockear para testing
Iterator Recorrer colecciones, lazy evaluation STL integration, range-based for loops, memory safety Overhead de indirección, complejidad de implementación
Mediator Comunicación compleja, GUI, sistemas distribuidos Encapsulation fuerte, thread safety, RAII Single responsibility violation, testing complexity
Memento Undo/redo, snapshots, serialization Move semantics, RAII, memory safety Memory overhead, serialization complexity

Consideraciones específicas de C++ para cada patrón

Observer en C++

// ✅ Recomendado: Usar std::shared_ptr para gestión automática de memoria
class Subject {
    std::vector<std::shared_ptr<Observer>> observers_;
public:
    void attach(std::shared_ptr<Observer> observer) {
        observers_.push_back(observer);
    }
};

// ❌ Evitar: Raw pointers pueden causar memory leaks
class BadSubject {
    std::vector<Observer*> observers_;  // ¡Peligroso!
};

Strategy en C++

// ✅ Recomendado: Usar templates para type safety y performance
template <typename T>
class SortStrategy {
    virtual std::vector<T> sort(const std::vector<T>&) = 0;
};

// ✅ Alternativa: Usar std::function para máxima flexibilidad
using SortFunction = std::function<void(std::vector<int>&)>;
class FlexibleSortStrategy {
    SortFunction sort_func_;
public:
    void setSortFunction(SortFunction func) { sort_func_ = func; }
};

Command en C++

// ✅ Recomendado: Usar move semantics y RAII
class Command {
    std::unique_ptr<Receiver> receiver_;  // RAII
public:
    Command(std::unique_ptr<Receiver> r) : receiver_(std::move(r)) {}
    void execute() { /* usar receiver_ */ }
};

// ✅ Usar lambdas para comandos simples
auto simple_command = std::make_shared<LambdaCommand>(
    []() { std::cout << "Ejecutando lambda" << std::endl; },
    []() { std::cout << "Deshaciendo lambda" << std::endl; }
);

State en C++

// ✅ Recomendado: State pattern con stack allocation
class Context {
    std::unique_ptr<State> current_state_;
public:
    void changeState(std::unique_ptr<State> new_state) {
        current_state_ = std::move(new_state);
    }
};

// ✅ Alternativa: Usar enum class para estados simples
enum class PlayerState { Stopped, Playing, Paused };
class SimplePlayer {
    PlayerState state_ = PlayerState::Stopped;
public:
    void play() {
        if (state_ == PlayerState::Stopped || state_ == PlayerState::Paused) {
            state_ = PlayerState::Playing;
        }
    }
};

Template Method en C++

// ✅ Recomendado: Non-Virtual Interface (NVI) idiom
class Algorithm {
public:
    void process() {  // Template method - no virtual
        setup();
        doProcess();  // Virtual hook
        cleanup();
    }
private:
    virtual void doProcess() = 0;
    void setup() { /* default implementation */ }
    void cleanup() { /* default implementation */ }
};

// ✅ Usar CRTP para static polymorphism
template <typename Derived>
class CRTPAlgorithm {
public:
    void process() {
        static_cast<Derived*>(this)->doProcess();
    }
};

Iterator en C++

// ✅ Recomendado: Usar STL iterator concepts
class MyContainer {
public:
    class iterator {
        using iterator_category = std::forward_iterator_tag;
        using value_type = MyType;
        // ... otros typedefs requeridos
    };
    iterator begin() { return iterator(data_.begin()); }
    iterator end() { return iterator(data_.end()); }
private:
    std::vector<MyType> data_;
};

// ✅ Usar range-based for loops
for (const auto& item : my_container) {
    // procesar item
}

Mediator en C++

// ✅ Recomendado: Usar weak_ptr para evitar circular references
class Mediator {
    std::unordered_map<std::string, std::weak_ptr<Colleague>> colleagues_;
public:
    void registerColleague(const std::string& name, std::shared_ptr<Colleague> c) {
        colleagues_[name] = c;
    }
};

// ✅ Usar para GUI frameworks
class DialogMediator {
    std::shared_ptr<Button> ok_button_;
    std::shared_ptr<TextField> input_field_;
public:
    void buttonClicked() {
        if (input_field_->isValid()) {
            ok_button_->enable();
        }
    }
};

Memento en C++

// ✅ Recomendado: Usar smart pointers y move semantics
class Memento {
    std::unique_ptr<InternalState> state_;
public:
    Memento(std::unique_ptr<InternalState> s) : state_(std::move(s)) {}
    const InternalState& getState() const { return *state_; }
};

// ✅ Usar para undo/redo en editores
class TextEditor {
    std::stack<std::unique_ptr<DocumentMemento>> undo_stack_;
    std::stack<std::unique_ptr<DocumentMemento>> redo_stack_;
public:
    void saveState() {
        undo_stack_.push(createMemento());
        // Clear redo stack when new action is performed
        while (!redo_stack_.empty()) redo_stack_.pop();
    }
};

Paso 11: Proyecto práctico - Editor de texto

Vamos a crear un editor de texto completo que combine múltiples patrones de comportamiento en C++.

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <stack>
#include <functional>
#include <unordered_map>
#include <chrono>
#include <thread>

// Memento para estados del documento
struct DocumentState {
    std::string contenido;
    size_t cursor_posicion;
    std::pair<size_t, size_t> seleccion;

    DocumentState(const std::string& cont, size_t cursor, std::pair<size_t, size_t> sel = {0, 0})
        : contenido(cont), cursor_posicion(cursor), seleccion(sel) {}
};

// Command para operaciones del editor
class EditorCommand {
public:
    virtual ~EditorCommand() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};

// Observer para notificaciones de cambios
class EditorObserver {
public:
    virtual ~EditorObserver() = default;
    virtual void update(const std::string& event_type) = 0;
};

// State para modos del editor
class EditorState {
public:
    virtual ~EditorState() = default;
    virtual void handleKey(class TextEditor& editor, char key) = 0;
    virtual std::string getName() const = 0;
};

// Strategy para diferentes estrategias de guardado
class SaveStrategy {
public:
    virtual ~SaveStrategy() = default;
    virtual void save(const std::string& contenido, const std::string& nombre_archivo) = 0;
};

// Editor principal que combina múltiples patrones
class TextEditor {
private:
    std::string contenido_;
    size_t cursor_posicion_;
    std::unique_ptr<EditorState> estado_actual_;
    std::vector<std::shared_ptr<EditorObserver>> observadores_;
    std::vector<std::shared_ptr<EditorCommand>> historial_comandos_;
    int indice_historial_;
    std::unordered_map<std::string, std::unique_ptr<SaveStrategy>> estrategias_guardado_;

public:
    TextEditor() : contenido_(""), cursor_posicion_(0), indice_historial_(-1) {
        cambiarEstado(std::make_unique<NormalState>());
    }

    // Observer Pattern
    void agregarObservador(std::shared_ptr<EditorObserver> observador) {
        observadores_.push_back(observador);
    }

    void notificarObservadores(const std::string& evento) {
        for (const auto& observador : observadores_) {
            observador->update(evento);
        }
    }

    // State Pattern
    void cambiarEstado(std::unique_ptr<EditorState> nuevo_estado) {
        estado_actual_ = std::move(nuevo_estado);
        std::cout << "🔄 Modo cambiado a: " << estado_actual_->getName() << std::endl;
    }

    void procesarTecla(char tecla) {
        estado_actual_->handleKey(*this, tecla);
    }

    // Command Pattern
    void ejecutarComando(std::shared_ptr<EditorCommand> comando) {
        comando->execute();

        // Agregar al historial (remover comandos "futuros" si estamos en medio del historial)
        if (indice_historial_ + 1 < static_cast<int>(historial_comandos_.size())) {
            historial_comandos_.resize(indice_historial_ + 1);
        }
        historial_comandos_.push_back(comando);
        indice_historial_++;

        notificarObservadores("contenido_cambiado");
    }

    void deshacer() {
        if (indice_historial_ >= 0) {
            historial_comandos_[indice_historial_]->undo();
            indice_historial_--;
            notificarObservadores("contenido_cambiado");
            std::cout << "🔄 Comando deshecho" << std::endl;
        }
    }

    // Strategy Pattern
    void registrarEstrategiaGuardado(const std::string& nombre, std::unique_ptr<SaveStrategy> estrategia) {
        estrategias_guardado_[nombre] = std::move(estrategia);
    }

    void guardar(const std::string& nombre_archivo, const std::string& estrategia = "local") {
        auto it = estrategias_guardado_.find(estrategia);
        if (it != estrategias_guardado_.end()) {
            it->second->save(contenido_, nombre_archivo);
        } else {
            std::cout << "❌ Estrategia de guardado no encontrada: " << estrategia << std::endl;
        }
    }

    // Métodos de edición
    void escribirTexto(const std::string& texto) {
        contenido_ = contenido_.substr(0, cursor_posicion_) +
                    texto +
                    contenido_.substr(cursor_posicion_);
        cursor_posicion_ += texto.length();
        std::cout << "📝 Escrito: '" << texto << "'" << std::endl;
    }

    void borrarCaracter(size_t cantidad = 1) {
        if (cursor_posicion_ >= cantidad) {
            size_t inicio = cursor_posicion_ - cantidad;
            size_t fin = cursor_posicion_;
            std::string texto_borrado = contenido_.substr(inicio, cantidad);

            contenido_ = contenido_.substr(0, inicio) + contenido_.substr(fin);
            cursor_posicion_ -= cantidad;

            std::cout << "⌫ Borrado: '" << texto_borrado << "'" << std::endl;
        } else {
            std::cout << "No hay texto para borrar" << std::endl;
        }
    }

    void moverCursor(size_t posicion) {
        if (posicion <= contenido_.length()) {
            cursor_posicion_ = posicion;
            notificarObservadores("cursor_movido");
            std::cout << "📍 Cursor movido a posición " << posicion << std::endl;
        }
    }

    // Getters
    const std::string& getContenido() const { return contenido_; }
    size_t getCursorPosicion() const { return cursor_posicion_; }
    const std::string& getEstadoActual() const { return estado_actual_->getName(); }

    void mostrarEstado() const {
        std::string contenido_mostrado = contenido_;
        if (cursor_posicion_ < contenido_mostrado.length()) {
            contenido_mostrado = contenido_mostrado.substr(0, cursor_posicion_) + "|" +
                               contenido_mostrado.substr(cursor_posicion_);
        } else {
            contenido_mostrado += "|";
        }
        std::cout << "📄 [" << estado_actual_->getName() << "] '" << contenido_mostrado << "'" << std::endl;
    }
};

// Estados concretos
class NormalState : public EditorState {
public:
    void handleKey(TextEditor& editor, char key) override {
        if (key == 'i') {
            editor.cambiarEstado(std::make_unique<InsertState>());
        } else if (key == 'x') {
            editor.borrarCaracter();
        } else if (key == 'u') {
            editor.deshacer();
        }
    }

    std::string getName() const override {
        return "NORMAL";
    }
};

class InsertState : public EditorState {
public:
    void handleKey(TextEditor& editor, char key) override {
        if (key == 27) {  // Escape key
            editor.cambiarEstado(std::make_unique<NormalState>());
        } else if (key == 127 || key == 8) {  // Backspace
            editor.borrarCaracter();
        } else if (key >= 32 && key <= 126) {  // Caracter imprimible
            editor.escribirTexto(std::string(1, key));
        }
    }

    std::string getName() const override {
        return "INSERTAR";
    }
};

// Comandos concretos
class InsertCommand : public EditorCommand {
private:
    std::shared_ptr<TextEditor> editor_;
    std::string texto_;
    size_t posicion_;
    std::string texto_anterior_;

public:
    InsertCommand(std::shared_ptr<TextEditor> editor, const std::string& texto, size_t posicion = std::string::npos)
        : editor_(editor), texto_(texto), posicion_(posicion) {}

    void execute() override {
        if (posicion_ != std::string::npos) {
            editor_->moverCursor(posicion_);
        }

        // Guardar texto que será reemplazado
        size_t longitud_texto = texto_.length();
        size_t inicio = editor_->getCursorPosicion();
        size_t fin = inicio + longitud_texto;
        texto_anterior_ = editor_->getContenido().substr(inicio, longitud_texto);

        editor_->escribirTexto(texto_);
    }

    void undo() override {
        // Remover el texto insertado y restaurar el anterior
        size_t inicio = editor_->getCursorPosicion() - texto_.length();
        size_t fin = editor_->getCursorPosicion();
        const std::string& contenido = editor_->getContenido();

        std::string nuevo_contenido = contenido.substr(0, inicio) +
                                    texto_anterior_ +
                                    contenido.substr(fin);
        // Nota: No podemos modificar directamente el contenido, necesitamos un método
        std::cout << "Deshaciendo inserción de '" << texto_ << "'" << std::endl;
    }
};

class DeleteCommand : public EditorCommand {
private:
    std::shared_ptr<TextEditor> editor_;
    size_t cantidad_;
    std::string texto_borrado_;
    size_t posicion_original_;

public:
    DeleteCommand(std::shared_ptr<TextEditor> editor, size_t cantidad = 1)
        : editor_(editor), cantidad_(cantidad) {}

    void execute() override {
        posicion_original_ = editor_->getCursorPosicion();
        texto_borrado_ = editor_->getContenido().substr(posicion_original_ - cantidad_, cantidad_);
        editor_->borrarCaracter(cantidad_);
    }

    void undo() override {
        std::cout << "Deshaciendo borrado de '" << texto_borrado_ << "'" << std::endl;
    }
};

// Observadores concretos
class ConsoleObserver : public EditorObserver {
public:
    void update(const std::string& event_type) override {
        if (event_type == "contenido_cambiado") {
            std::cout << "📝 Contenido actualizado" << std::endl;
        } else if (event_type == "cursor_movido") {
            std::cout << "📍 Cursor movido" << std::endl;
        }
    }
};

class FileObserver : public EditorObserver {
private:
    std::string nombre_archivo_;
    std::chrono::steady_clock::time_point ultimo_guardado_;

public:
    FileObserver(const std::string& nombre_archivo) : nombre_archivo_(nombre_archivo) {
        ultimo_guardado_ = std::chrono::steady_clock::now();
    }

    void update(const std::string& event_type) override {
        if (event_type == "contenido_cambiado") {
            auto ahora = std::chrono::steady_clock::now();
            auto tiempo_transcurrido = std::chrono::duration_cast<std::chrono::seconds>(ahora - ultimo_guardado_).count();

            if (tiempo_transcurrido > 5) {  // Auto-guardar cada 5 segundos
                std::cout << "💾 Auto-guardando en " << nombre_archivo_ << std::endl;
                ultimo_guardado_ = ahora;
            }
        }
    }
};

// Estrategias de guardado concretas
class LocalSaveStrategy : public SaveStrategy {
public:
    void save(const std::string& contenido, const std::string& nombre_archivo) override {
        std::cout << "💾 Guardando localmente en " << nombre_archivo << std::endl;
        std::cout << "   Contenido: " << contenido.substr(0, 50) << "..." << std::endl;
    }
};

class CloudSaveStrategy : public SaveStrategy {
public:
    void save(const std::string& contenido, const std::string& nombre_archivo) override {
        std::cout << "☁️ Guardando en la nube: " << nombre_archivo << std::endl;
        std::cout << "   Subiendo " << contenido.length() << " caracteres..." << std::endl;
    }
};

// Demo del editor completo
void demo_editor() {
    std::cout << "=== Editor de Texto Interactivo con Múltiples Patrones ===" << std::endl;

    // Crear editor
    auto editor = std::make_shared<TextEditor>();

    // Agregar observadores
    auto observador_consola = std::make_shared<ConsoleObserver>();
    auto observador_archivo = std::make_shared<FileObserver>("documento.txt");

    editor->agregarObservador(observador_consola);
    editor->agregarObservador(observador_archivo);

    // Registrar estrategias de guardado
    editor->registrarEstrategiaGuardado("local", std::make_unique<LocalSaveStrategy>());
    editor->registrarEstrategiaGuardado("nube", std::make_unique<CloudSaveStrategy>());

    editor->mostrarEstado();

    // Simular entrada del usuario
    std::vector<char> comandos = {
        'i',        // Entrar en modo inserción
        'H', 'o', 'l', 'a', ' ', 'm', 'u', 'n', 'd', 'o',
        27,         // Escape - Volver a modo normal
        'x',        // Borrar carácter
        'u',        // Deshacer
    };

    for (char comando : comandos) {
        std::cout << "\n🎹 Presionando tecla: '" << comando << "'" << std::endl;
        editor->procesarTecla(comando);
        editor->mostrarEstado();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    std::cout << "\n=== Estrategias de Guardado ===" << std::endl;

    // Probar diferentes estrategias de guardado
    editor->guardar("mi_documento.txt", "local");
    editor->guardar("mi_documento.txt", "nube");
}

// Uso
int main() {
    demo_editor();
    return 0;
}

Conclusión

¡Has dominado los patrones de diseño de comportamiento en C++! Estos patrones te permiten gestionar algoritmos, estados y comunicaciones entre objetos de manera flexible, mantenible y eficiente, aprovechando las fortalezas únicas del lenguaje C++.

Ventajas de implementar patrones de comportamiento en C++

  • Type Safety: El sistema de tipos fuerte de C++ detecta errores en tiempo de compilación
  • Performance: Los patrones se ejecutan con overhead mínimo gracias al inlining y optimizaciones del compilador
  • Memory Management: Control preciso sobre la memoria con RAII y smart pointers
  • Multi-threading: Soporte nativo para concurrencia en patrones como Observer
  • Templates: Permiten implementar patrones genéricos y reutilizables
  • Zero-cost Abstractions: Los patrones bien diseñados no añaden overhead en runtime

Consejos para aplicar patrones de comportamiento en C++

  1. Empieza simple: No todos los problemas requieren patrones complejos
  2. Usa smart pointers: std::shared_ptr, std::unique_ptr para gestión automática de memoria
  3. Aprovecha STL: Muchos patrones ya están implementados en la Standard Template Library
  4. Considera el ownership: Usa move semantics y RAII para recursos
  5. Piensa en thread safety: Especialmente para Observer y Mediator
  6. Usa templates: Para patrones genéricos y type-safe
  7. Evita memory leaks: Siempre limpia recursos apropiadamente

Practica aplicando estos patrones en proyectos reales y combina diferentes patrones según las necesidades específicas de tu aplicación. Recuerda que C++ te permite implementar estos patrones con un control y eficiencia que otros lenguajes no pueden igualar.

Para más tutoriales sobre patrones de diseño y arquitectura avanzada en C++, visita nuestra sección de tutoriales.


¡Sigue practicando y aplicando estos patrones en proyectos reales de C++!


💡 Tip Importante

📝 Mejores Prácticas en Patrones de Comportamiento con C++

  • Elige el patrón adecuado: Analiza el problema de comunicación/algoritmo antes de seleccionar un patrón
  • Usa smart pointers: std::shared_ptr para Observer, std::unique_ptr para Strategy
  • Aprovecha RAII: Para cleanup automático en Command y Memento
  • Implementa thread safety: Especialmente en Observer y Mediator
  • Usa templates: Para patrones genéricos y type-safe
  • Considera el ownership: Move semantics para eficiencia
  • Evita memory leaks: Siempre usa RAII y smart pointers
  • Testing: Los patrones deben facilitar el testing unitario
  • Documenta decisiones: Explica por qué elegiste ciertos patrones en tu código
  • Evita over-engineering: No uses patrones complejos para problemas simples
  • Considera el rendimiento: Algunos patrones pueden afectar el rendimiento
  • Usa STL cuando sea posible: Muchos patrones ya están implementados eficientemente

📚 Recursos Recomendados para C++:

🎯 Ejemplos de proyectos donde aplicar estos patrones:

  • Observer: Sistemas de eventos, GUIs, sistemas reactivos
  • Strategy: Algoritmos de ordenamiento, políticas de negocio
  • Command: Editores de texto, sistemas de undo/redo
  • State: Máquinas de estado, parsers, protocolos de red
  • Template Method: Frameworks, algoritmos con pasos variables
  • Iterator: Contenedores personalizados, lazy evaluation
  • Mediator: Sistemas de chat, componentes GUI complejos
  • Memento: Editores, sistemas de versionado, snapshots

¡Estos patrones te ayudarán a crear software C++ robusto, eficiente y mantenible!

Toca los botones para interactuar

Comentarios

Comentarios

Inicia sesión para dejar un comentario.

No hay comentarios aún

Sé el primero en comentar este tutorial.

Tutoriales Relacionados

Descubre más tutoriales relacionados que podrían ser de tu interés

Imagen destacada del tutorial relacionado: Principios SOLID de Diseño de Software en C++
Patrones de Diseño

Principios SOLID de Diseño de Software en C++

Aprende los principios SOLID de diseño de software (SRP, OCP, LSP, ISP, DIP) con ejemplos prácticos en C++ y mejora la calidad de tu código con type safety y performance.

José Elías Romero Guanipa
01 Sep 2025
Imagen destacada del tutorial relacionado: Patrones de Diseño Creacionales en C++
Patrones de Diseño

Patrones de Diseño Creacionales en C++

Aprende patrones de diseño creacionales como Singleton, Factory, Builder y Prototype con ejemplos prácticos en C++ moderno, aprovechando smart pointers, RAII y características del lenguaje.

José Elías Romero Guanipa
03 Sep 2025
Imagen destacada del tutorial relacionado: Patrones de Diseño Estructurales en C++
Patrones de Diseño

Patrones de Diseño Estructurales en C++

Aprende patrones de diseño estructurales como Adapter, Decorator, Facade y Proxy con ejemplos prácticos en C++, aprovechando las fortalezas del lenguaje para estructuras robustas y eficientes.

José Elías Romero Guanipa
04 Sep 2025
Imagen destacada del tutorial relacionado: Patrones de Diseño - Aplicaciones Avanzadas
Patrones de Diseño

Patrones de Diseño - Aplicaciones Avanzadas

Aprende aplicaciones avanzadas de patrones de diseño en arquitecturas empresariales como microservicios, CQRS, event sourcing y más con ejemplos prácticos en C++.

José Elías Romero Guanipa
05 Sep 2025
Foto de perfil del autor José Elías Romero Guanipa
José Elías Romero Guanipa
Autor

🌟 Nube de Etiquetas

Descubre temas populares en nuestros tutoriales

python
python 25 tutoriales
poo
poo 8 tutoriales
ciencia de datos
ciencia de datos 8 tutoriales
patrones diseño
patrones diseño 7 tutoriales
matplotlib
matplotlib 7 tutoriales
pandas
pandas 6 tutoriales
visualizacion
visualizacion 6 tutoriales
principiante
principiante 5 tutoriales
numpy
numpy 5 tutoriales
c++
c++ 5 tutoriales
estadistica
estadistica 4 tutoriales
cpp
cpp 4 tutoriales
bases de datos
bases de datos 4 tutoriales
dataframe
dataframe 4 tutoriales
csv
csv 3 tutoriales
json
json 3 tutoriales
machine learning
machine learning 3 tutoriales
rendimiento
rendimiento 3 tutoriales
mysql
mysql 3 tutoriales
postgresql
postgresql 3 tutoriales
analisis de datos
analisis de datos 3 tutoriales
graficos
graficos 3 tutoriales
excepciones
excepciones 2 tutoriales
algoritmos
algoritmos 2 tutoriales
estructuras datos
estructuras datos 2 tutoriales

Las etiquetas más grandes y brillantes aparecen en más tutoriales

logo logo

©2024 ViveBTC