Padrão Singleton
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 91 de 104.
O padrão Singleton garante que uma classe tenha apenas uma instância durante todo o programa e fornece um ponto de acesso global a ela. Isso é útil para recursos que devem ser compartilhados, como um gerenciador de configurações, um logger ou um pool de conexões de banco de dados.
A ideia principal é tornar o construtor privado para que ninguém possa criar instâncias diretamente e, então, fornecer um método estático que retorne a única instância:
#include <iostream>
#include <string>
class Logger {
private:
std::string logFile;
// Construtor privado - impede a instanciação direta
Logger() : logFile("app.log") {}
// Exclui o construtor de cópia e o operador de atribuição
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
public:
// Método estático para acessar a única instância
static Logger& getInstance() {
static Logger instance; // Criado uma vez, vive até o fim do programa
return instance;
}
void log(const std::string& message) {
std::cout << "[LOG] " << message << "\n";
}
};
int main() {
Logger::getInstance().log("Application started");
Logger& logger = Logger::getInstance();
logger.log("Processing data");
}O static Logger instance dentro de getInstance() é um recurso do C++11 chamado de "estáticos mágicos" — ele é seguro para threads e tem a garantia de ser inicializado apenas uma vez. Ao excluir o construtor de cópia e o operador de atribuição, impedimos que alguém crie cópias do nosso singleton.
Use Singleton com moderação — ele introduz um estado global, o que pode dificultar os testes. Ele é mais adequado para recursos realmente compartilhados, nos quais ter várias instâncias causaria problemas.
Desafio
FácilVamos criar um singleton GameSettings que gerencia a configuração de um aplicativo de jogo. Este é um caso de uso perfeito para o padrão Singleton. Você deseja exatamente um gerenciador de configurações ao qual todas as partes do seu jogo possam ter acesso, garantindo uma configuração consistente em todo o projeto.
Você organizará seu código em três arquivos:
GameSettings.h: Defina sua classe singleton que gerencia a configuração do jogo.Sua classe
GameSettingsdeve armazenar três configurações como membros privados:difficulty(string),volume(inteiro de 0-100) efullscreen(booleano). Inicialize-as com os valores padrão:"Normal",50efalse.Implemente o padrão Singleton corretamente:
- Torne o construtor privado
- Exclua o construtor de cópia e o operador de atribuição
- Forneça um método estático
getInstance()que retorne uma referência à única instância
Adicione estes métodos públicos para interagir com as configurações:
setDifficulty(const std::string& diff): atualiza a dificuldadesetVolume(int vol): atualiza o volumesetFullscreen(bool fs): atualiza o modo de tela cheiadisplaySettings(): exibe todas as configurações atuais
GameModule.h: Crie um módulo separado que acesse o singleton.Defina uma função chamada
applyGamePresetque receba um nome de predefinição (string). Essa função deve acessar o singletonGameSettingse configurá-lo com base na predefinição:"casual": define a dificuldade como"Easy", o volume como70e a tela cheia comofalse"competitive": define a dificuldade como"Hard", o volume como30e a tela cheia comotrue- Qualquer outra predefinição: mantém as configurações padrão inalteradas
Isso demonstra como diferentes partes de um programa podem acessar e modificar a mesma instância singleton.
main.cpp: Reúna tudo para mostrar o singleton em ação.Leia duas entradas:
- Um nome de predefinição (string)
- Um nível de volume personalizado (inteiro)
Primeiro, exiba as configurações padrão iniciais chamando
displaySettings()no singleton. Imprima uma linha em branco depois.Em seguida, aplique a predefinição usando sua função
applyGamePresete exiba as configurações novamente. Imprima uma linha em branco depois.Por fim, substitua apenas o volume pelo valor de entrada personalizado e exiba as configurações finais.
O método displaySettings() deve produzir exatamente este formato:
Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]Por exemplo, com as entradas casual e 85:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Easy
Volume: 70
Fullscreen: no
Difficulty: Easy
Volume: 85
Fullscreen: noCom as entradas competitive e 20:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Hard
Volume: 30
Fullscreen: yes
Difficulty: Hard
Volume: 20
Fullscreen: yesObserve como tanto main.cpp quanto GameModule.h acessam a mesma instância singleton: as alterações feitas em um lugar ficam visíveis em todos os outros. Esse é o poder do padrão Singleton para recursos compartilhados, como as configurações do jogo.
Experimente você mesmo
#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"
using namespace std;
int main() {
// Ler entradas
string presetName;
int customVolume;
cin >> presetName;
cin >> customVolume;
// TODO: Obter a instância singleton de GameSettings
// TODO: Exibir as configurações padrão iniciais
// TODO: Imprimir uma linha em branco
// TODO: Aplicar o preset usando a função applyGamePreset
// TODO: Exibir as configurações após aplicar o preset
// TODO: Imprimir uma linha em branco
// TODO: Sobrescrever o volume com customVolume
// TODO: Exibir as configurações finais
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 de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online