Menu
Coddy logo textTech

Patrón Singleton

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 91 de 104.

El patrón Singleton garantiza que una clase tenga una sola instancia en todo el programa y proporciona un punto de acceso global a ella. Esto resulta útil para recursos que deben compartirse, como un administrador de configuración, un registrador o un grupo de conexiones de base de datos.

La idea clave es hacer privado el constructor para que nadie pueda crear instancias directamente y después proporcionar un método estático que devuelva la única instancia:

#include <iostream>
#include <string>

class Logger {
private:
    std::string logFile;
    
    // Constructor privado - evita la instanciación directa
    Logger() : logFile("app.log") {}
    
    // Elimina el constructor de copia y el operador de asignación
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // Método estático para acceder a la única instancia
    static Logger& getInstance() {
        static Logger instance;  // Creado una vez, vive hasta que el programa termina
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

int main() {
    Logger::getInstance().log("Application started");
    
    Logger& logger = Logger::getInstance();
    logger.log("Processing data");
}

El static Logger instance dentro de getInstance() es una característica de C++11 llamada «estáticos mágicos»: es segura para subprocesos y se garantiza que se inicialice una sola vez. Al eliminar el constructor de copia y el operador de asignación, evitamos que alguien cree copias de nuestro singleton.

Usa Singleton con moderación: introduce un estado global que puede dificultar las pruebas. Es más adecuado para recursos verdaderamente compartidos, en los que tener varias instancias causaría problemas.

challenge icon

Desafío

Fácil

Construyamos un singleton GameSettings que administre la configuración de una aplicación de juego. Este es un caso de uso perfecto para el patrón Singleton. Quieres exactamente un administrador de configuración al que puedan acceder todas las partes de tu juego, garantizando una configuración coherente en todo momento.

Organizarás tu código en tres archivos:

  • GameSettings.h: Define tu clase singleton que administra la configuración del juego.

    Tu clase GameSettings debe almacenar tres configuraciones como miembros privados: difficulty (cadena), volume (entero de 0-100) y fullscreen (booleano). Inicialízalas con los valores predeterminados: "Normal", 50 y false.

    Implementa correctamente el patrón Singleton:

    • Haz privado el constructor
    • Elimina el constructor de copia y el operador de asignación
    • Proporciona un método estático getInstance() que devuelva una referencia a la única instancia

    Añade estos métodos públicos para interactuar con la configuración:

    • setDifficulty(const std::string& diff): actualiza la dificultad
    • setVolume(int vol): actualiza el volumen
    • setFullscreen(bool fs): actualiza el modo de pantalla completa
    • displaySettings(): muestra toda la configuración actual
  • GameModule.h: Crea un módulo independiente que acceda al singleton.

    Define una función llamada applyGamePreset que reciba un nombre de preajuste (cadena). Esta función debe acceder al singleton GameSettings y configurarlo según el preajuste:

    • "casual": establece la dificultad en "Easy", el volumen en 70 y la pantalla completa en false
    • "competitive": establece la dificultad en "Hard", el volumen en 30 y la pantalla completa en true
    • Cualquier otro preajuste: mantiene sin cambios la configuración predeterminada

    Esto demuestra cómo distintas partes de un programa pueden acceder a la misma instancia singleton y modificarla.

  • main.cpp: Reúne todo para mostrar el singleton en acción.

    Lee dos entradas:

    1. Un nombre de preajuste (cadena)
    2. Un nivel de volumen personalizado (entero)

    Primero, muestra la configuración predeterminada inicial llamando a displaySettings() en el singleton. Imprime una línea en blanco después.

    Después, aplica el preajuste mediante tu función applyGamePreset y vuelve a mostrar la configuración. Imprime una línea en blanco después.

    Por último, sobrescribe únicamente el volumen con el valor de entrada personalizado y muestra la configuración final.

El método displaySettings() debe mostrar exactamente este formato:

Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]

Por ejemplo, con las entradas casual y 85:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Easy
Volume: 70
Fullscreen: no

Difficulty: Easy
Volume: 85
Fullscreen: no

Con las entradas competitive y 20:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Hard
Volume: 30
Fullscreen: yes

Difficulty: Hard
Volume: 20
Fullscreen: yes

Observa cómo tanto main.cpp como GameModule.h acceden a la misma instancia singleton: los cambios realizados en un lugar son visibles en todas partes. Este es el poder del patrón Singleton para recursos compartidos como la configuración de un juego.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"

using namespace std;

int main() {
    // Leer entradas
    string presetName;
    int customVolume;
    cin >> presetName;
    cin >> customVolume;
    
    // TODO: Obtener la instancia singleton de GameSettings
    
    // TODO: Mostrar la configuración predeterminada inicial
    
    // TODO: Imprimir una línea en blanco
    
    // TODO: Aplicar el preset usando la función applyGamePreset
    
    // TODO: Mostrar la configuración después de aplicar el preset
    
    // TODO: Imprimir una línea en blanco
    
    // TODO: Sobrescribir el volumen con customVolume
    
    // TODO: Mostrar la configuración final
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C++ online