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 de logs ou uma conexão com o banco de dados. O padrão Singleton garante que exista apenas uma instância 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 a esse arquivo e persiste durante toda a execução do programa. Combinada 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. Todas as chamadas subsequentes retornam 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.
No 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ê chame get_config(), você sempre obtém o mesmo objeto.
Desafio
FácilVamos criar um módulo Settings que gerencia a configuração de todo o aplicativo usando o padrão Singleton. Não importa quantas vezes seu código solicite as configurações, ele sempre deve 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 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). As proteções de inclusão são essenciais aqui.settings.c: É aqui que a mágica acontece. Defina a structSettingsreal com os camposvolumeebrightness. Crie uma variável ponteirostaticpara armazenar a única instância (inicializada comoNULL). Implementeget_settingspara que a primeira chamada aloque e inicialize a instância com valores padrão (volume: 50, brightness: 75), enquanto as chamadas seguintes simplesmente 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 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 ambos os valores. Se o Singleton estiver funcionando, as alterações feitas por meio de um ponteiro serão visíveis pelo 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 static dentro do seu arquivo-fonte garante que exista apenas uma instância e que ela persista durante toda a execução 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 os resultados em duas variáveis de ponteiro diferentes
// TODO: Use o primeiro ponteiro para atualizar o volume e o brilho com os valores de entrada
// TODO: Use o segundo ponteiro para obter e imprimir o volume e o brilho
// (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çalhoProteções de inclusãoArquivos-fonteFunções estáticasRevisão: calculadora modular4Encapsulamento
Conceito de ponteiros opacosDefinindo structs opacasGetters e settersValidação em settersRevisão: caixa secreta2Objetos e métodos
Structs como objetosO ponteiro 'Self'Correção de constPonteiro vs. valorMétodos auxiliaresRevisão: Gerenciador de pontos5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de vida do objeto
Padrão de construtorPadrão de destrutorInicialização da pilhaCópia profundaRevisão: wrapper de String6Herança por composição
Incorporação de structRegra do primeiro membroAcessando membros do tipo paiUpcastingRevisão: hierarquia de formas9Projeto: Desenhador de Formas
Visão geral do projetoImplementação do círculoImplementação do retânguloUso polimórficoContêiner de formasPratique por conta própria: Compilador de C online