Padrão Singleton
Parte da seção Programação Orientada a Objetos 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ção, um sistema de registro ou uma conexão com o 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-fonte. Lembre-se de que static no escopo do arquivo significa que a variável é privada para esse arquivo e persiste durante toda a vida útil do programa. Combinado com uma função getter, isso cria 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 retenha seu valor entre chamadas de função e permaneça oculta de outros arquivos.
Do cabeçalho, os usuários veem apenas a função getter. Eles não podem acessar nem modificar a variável estática diretamente. Não importa quantas vezes ou de onde você chama get_config(), você sempre obtém o mesmo objeto.
Desafio
FácilVamos criar 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 deverá sempre receber exatamente a mesma instância.
Você organizará seu código em três arquivos:
settings.h: Declare um tipo opacoSettingsusando uma declaração antecipada (o corpo da estrutura 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). As proteções contra inclusão múltipla são essenciais aqui.settings.c: É aqui que a mágica acontece. Defina a estruturaSettingsreal com os camposvolumeebrightness. Crie uma variável ponteirostaticpara armazenar a única instância (inicializada comoNULL). Implementeget_settingsde modo que a primeira chamada aloque e inicialize a instância com valores padrão (volume: 50, brilho: 75), enquanto as chamadas subsequentes simplesmente retornem 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 que representam novos valores de volume e brilho. Chameget_settingsduas vezes, armazenando os resultados em duas variáveis 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 os dois valores. Se o Singleton estiver funcionando, as alterações feitas por meio de um ponteiro estarão visíveis por meio do outro.
Seu programa receberá duas entradas:
- O novo valor do volume
- O novo valor do brilho
Imprima o volume e o brilho em linhas separadas depois de modificá-los por meio de um ponteiro e lê-los por meio 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 principal é 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 código-fonte garante que apenas uma instância exista, e ela persiste durante toda a vida útil do programa.
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 Programação Orientada a Objetos
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írculoImplementação do RetânguloUso PolimórficoContainer de FormasPratique por conta própria: Compilador de C online