Padrão Singleton
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 54 de 61.
Às vezes, você precisa de exatamente uma instância de um objeto em todo o seu programa — um gerenciador de configurações, um sistema de log ou uma conexão com banco de dados. O padrão Singleton garante que apenas uma instância exista e fornece um ponto de acesso global a ela.
Em C, implementamos isso usando uma variável static dentro de um arquivo de origem. Lembre-se de que static em escopo de arquivo significa que a variável é privada para aquele arquivo e persiste durante todo o tempo de vida do programa. Combinado com uma função getter, isso cria o nosso 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;
}A mágica acontece em get_config(). A primeira chamada cria e inicializa a instância. Cada chamada subsequente retorna o mesmo ponteiro.
A palavra-chave static garante que instance mantenha seu valor entre chamadas de função e permaneça oculta de outros arquivos.
A partir do cabeçalho, os usuários veem apenas a função getter — eles não podem acessar ou modificar a variável estática diretamente. Não importa quantas vezes ou de onde você chame get_config(), você sempre obtém o mesmo objeto.
Desafio
FácilVamos construir um módulo Settings que gerencia a configuração de toda a aplicação usando o padrão Singleton. Não importa quantas vezes seu código solicite as configurações, ele deve sempre receber a exata mesma instância.
Você organizará seu código em três arquivos:
settings.h: Declare um tipoSettingsopaco usando uma declaração antecipada (o corpo da struct permanece oculto). Declare uma funçãoget_settingsque retorna um ponteiro para a instância singleton, além de funções getter e setter para dois valores de configuração:volume(um inteiro) ebrightness(um inteiro). Include guards são essenciais aqui.settings.c: É aqui que a mágica acontece. Defina a structSettingsreal com os camposvolumeebrightness. Crie uma variável de ponteirostaticpara manter a instância única (inicializada comoNULL). Implementeget_settingsde modo que a primeira chamada aloque e inicialize a instância com valores padrão (volume: 50, brightness: 75), enquanto as chamadas subsequentes apenas retornam a instância existente. Implemente os getters e setters para acessar e modificar os campos ocultos.main.c: Demonstre que o Singleton funciona corretamente. Leia dois inteiros representando os novos valores de volume e brilho. Chameget_settingsduas vezes, armazenando os resultados em duas variáveis de ponteiro diferentes. Use o primeiro ponteiro para atualizar o volume e o brilho com os valores de entrada. Em seguida, use o segundo ponteiro para recuperar e imprimir ambos os valores. Se o Singleton estiver funcionando, as alterações feitas por meio de um ponteiro serão visíveis por meio do outro.
Seu programa receberá duas entradas:
- O novo valor de volume
- O novo valor de brilho
Imprima o volume e o brilho em linhas separadas após modificá-los através de um ponteiro e lê-los através de outro.
Exemplo de saída quando as entradas são 80 e 100:
80
100Exemplo de saída quando as entradas são 25 e 60:
25
60Exemplo de saída quando as entradas são 0 e 50:
0
50A ideia central é que ambos os ponteiros apontam para o mesmo objeto na memória — essa é a essência do padrão Singleton. A variável estática dentro do seu arquivo de origem garante que apenas uma instância exista, e ela persiste por toda a vida útil do programa.
Folha de consulta
O padrão Singleton garante que apenas uma instância de um objeto exista em todo o programa e fornece um ponto de acesso global a ele.
Em C, implemente um Singleton usando uma variável static dentro de um arquivo de origem combinada com uma função 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;
}Pontos-chave:
- A palavra-chave
staticno escopo do arquivo torna a variável privada para esse arquivo e persistente durante o tempo de vida do programa - A função getter verifica se a instância é
NULLna primeira chamada, cria e a inicializa, e então a retorna - Chamadas subsequentes retornam o mesmo ponteiro
- Os usuários só veem a função getter através do cabeçalho — eles não podem acessar ou modificar a variável estática diretamente
- Não importa quantas vezes ou de onde você chame o getter, você sempre obterá o mesmo objeto
Experimente você mesmo
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: Chame get_settings duas vezes e armazene em duas variáveis de ponteiro diferentes
// TODO: Use o primeiro ponteiro para atualizar volume e brightness com os valores de entrada
// TODO: Use o segundo ponteiro para recuperar e imprimir volume e brightness
// (Isso demonstra que ambos os ponteiros apontam para a mesma instância singleton)
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do Círculo