Patrón Singleton
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 54 de 61.
A veces necesitas exactamente una instancia de un objeto en todo tu programa: un gestor de configuración, un sistema de registro o una conexión a una base de datos. El patrón Singleton garantiza que solo exista una instancia y proporciona un punto de acceso global a ella.
En C, implementamos esto usando una variable static dentro de un archivo fuente. Recuerda que static en el ámbito del archivo significa que la variable es privada para ese archivo y persiste durante toda la vida del programa. Combinado con una función getter, esto crea nuestro Singleton:
// config.c
#include "config.h"
#include <stdlib.h>
struct Config {
int max_connections;
int timeout;
};
static Config* instance = NULL;
Config* get_config(void) {
if (instance == NULL) {
instance = malloc(sizeof(Config));
instance->max_connections = 100;
instance->timeout = 30;
}
return instance;
}La magia ocurre en get_config(). La primera llamada crea e inicializa la instancia. Cada llamada posterior devuelve el mismo puntero.
La palabra clave static asegura que instance conserve su valor entre llamadas a funciones y permanezca oculta para otros archivos.
Desde el encabezado, los usuarios solo ven la función getter; no pueden acceder ni modificar la variable estática directamente. No importa cuántas veces o desde dónde llames a get_config(), siempre obtendrás el mismo objeto.
Desafío
FácilVamos a construir un módulo Settings que gestione la configuración de toda la aplicación utilizando el patrón Singleton. No importa cuántas veces tu código solicite la configuración, siempre debe recibir exactamente la misma instancia.
Organizarás tu código en tres archivos:
settings.h: Declara un tipoSettingsopaco utilizando una declaración anticipada (el cuerpo de la estructura permanece oculto). Declara una funciónget_settingsque devuelva un puntero a la instancia singleton, además de funciones getter y setter para dos valores de configuración:volume(un entero) ybrightness(un entero). Los protectores de inclusión (include guards) son esenciales aquí.settings.c: Aquí es donde ocurre la magia. Define la estructuraSettingsreal con los camposvolumeybrightness. Crea una variable de punterostaticpara mantener la instancia única (inicializada enNULL). Implementaget_settingsde modo que la primera llamada asigne e inicialice la instancia con valores predeterminados (volume: 50, brightness: 75), mientras que las llamadas posteriores simplemente devuelvan la instancia existente. Implementa los getters y setters para acceder y modificar los campos ocultos.main.c: Demuestra que el Singleton funciona correctamente. Lee dos enteros que representen los nuevos valores de volumen y brillo. Llama aget_settingsdos veces, almacenando los resultados en dos variables de puntero diferentes. Usa el primer puntero para actualizar el volumen y el brillo con los valores de entrada. Luego, usa el segundo puntero para recuperar e imprimir ambos valores. Si el Singleton está funcionando, los cambios realizados a través de un puntero serán visibles a través del otro.
Tu programa recibirá dos entradas:
- El nuevo valor de volumen
- El nuevo valor de brillo
Imprime el volumen y el brillo en líneas separadas después de modificarlos a través de un puntero y leerlos a través de otro.
Ejemplo de salida cuando las entradas son 80 y 100:
80
100Ejemplo de salida cuando las entradas son 25 y 60:
25
60Ejemplo de salida cuando las entradas son 0 y 50:
0
50La idea clave es que ambos punteros apuntan al mismo objeto en la memoria; esa es la esencia del patrón Singleton. La variable estática dentro de tu archivo fuente asegura que solo exista una instancia, y esta persiste durante toda la vida del programa.
Hoja de referencia
El patrón Singleton asegura que solo exista una instancia de un objeto en todo el programa y proporciona un punto de acceso global a ella.
En C, implemente un Singleton utilizando una variable static dentro de un archivo fuente combinada con una función getter:
// config.c
#include "config.h"
#include <stdlib.h>
struct Config {
int max_connections;
int timeout;
};
static Config* instance = NULL;
Config* get_config(void) {
if (instance == NULL) {
instance = malloc(sizeof(Config));
instance->max_connections = 100;
instance->timeout = 30;
}
return instance;
}Puntos clave:
- La palabra clave
statica nivel de archivo hace que la variable sea privada para ese archivo y persistente durante la vida útil del programa - La función getter comprueba si la instancia es
NULLen la primera llamada, la crea e inicializa, y luego la devuelve - Las llamadas posteriores devuelven el mismo puntero
- Los usuarios solo ven la función getter a través del encabezado — no pueden acceder ni modificar la variable estática directamente
- No importa cuántas veces o desde dónde llame al getter, siempre obtendrá el mismo objeto
Pruébalo tú mismo
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: Llama a get_settings dos veces y almacena el resultado en dos variables de puntero diferentes
// TODO: Usa el primer puntero para actualizar volume y brightness con los valores de entrada
// TODO: Usa el segundo puntero para recuperar e imprimir volume y brightness
// (Esto demuestra que ambos punteros apuntan a la misma instancia singleton)
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo