Patrón Singleton
Parte de la sección Programación Orientada a Objetos 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 útil 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 garantiza que instance conserve su valor entre llamadas a funciones y permanezca oculta para otros archivos.
Desde el archivo de cabecera, los usuarios solo ven la función getter. No pueden acceder directamente a la variable estática ni modificarla. Sin importar cuántas veces ni desde dónde llames a get_config(), siempre obtienes el mismo objeto.
Desafío
FácilConstruyamos un módulo Settings que gestione la configuración de toda la aplicación mediante el patrón Singleton. Sin importar cuántas veces tu código solicite la configuración, siempre debería recibir exactamente la misma instancia.
Organizarás tu código en tres archivos:
settings.h: Declara un tipo opacoSettingsmediante 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). Las guardas de inclusión son esenciales aquí.settings.c: Aquí es donde ocurre la magia. Define la estructuraSettingsreal con los camposvolumeybrightness. Crea una variable punterostaticpara almacenar la única instancia (inicializada enNULL). Implementaget_settingsde modo que la primera llamada asigne memoria 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 a los campos ocultos y modificarlos.main.c: Demuestra que el Singleton funciona correctamente. Lee dos enteros que representan nuevos valores de volume y brightness. Llama dos veces aget_settings, almacenando los resultados en dos variables puntero diferentes. Usa el primer puntero para actualizar volume y brightness con los valores de entrada. Después, usa el segundo puntero para recuperar e imprimir ambos valores. Si el Singleton funciona, los cambios realizados mediante un puntero serán visibles a través del otro.
Tu programa recibirá dos entradas:
- El nuevo valor de volume
- El nuevo valor de brightness
Imprime volume y brightness en líneas separadas después de modificarlos mediante un puntero y leerlos mediante otro.
Salida de ejemplo cuando las entradas son 80 y 100:
80
100Salida de ejemplo cuando las entradas son 25 y 60:
25
60Salida de ejemplo cuando las entradas son 0 y 50:
0
50La idea clave es que ambos punteros apuntan al mismo objeto en memoria: esa es la esencia del patrón Singleton. La variable estática dentro de tu archivo fuente garantiza que solo exista una instancia y que persista durante toda la vida del programa.
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 en dos variables 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 Programación Orientada a Objetos
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írculoImplementación del rectánguloUso polimórficoContenedor de figurasPractica por tu cuenta: Compilador de C online