Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 tipo Settings opaco usando uma declaração antecipada (o corpo da struct permanece oculto). Declare uma função get_settings que 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) e brightness (um inteiro). Include guards são essenciais aqui.
  • settings.c: É aqui que a mágica acontece. Defina a struct Settings real com os campos volume e brightness. Crie uma variável de ponteiro static para manter a instância única (inicializada como NULL). Implemente get_settings de 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. Chame get_settings duas 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:

  1. O novo valor de volume
  2. 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
100

Exemplo de saída quando as entradas são 25 e 60:

25
60

Exemplo de saída quando as entradas são 0 e 50:

0
50

A 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 static no 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 é NULL na 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;
}
quiz iconTeste seus conhecimentos

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