Há casos em que você deseja executar uma inicialização pesada apenas no primeiro acesso e reutilizar o valor gerado posteriormente. Este pattern implementa isso de forma simples, sem a sobrecarga de um cache de memória.
Código
export interface Reusable<T> { value: T;}
export function reusable<T>(init: () => T): Reusable<T> { return { get value() { const value = init(); Object.defineProperty(this, "value", { value }); return value; }, };}Exemplos de Uso
Timestamp Fixo
const timestamp = reusable(() => new Date());
// Date é gerado no primeiro acessoconsole.log(timestamp.value); // 2025-01-18T09:00:00.000Z
// O mesmo valor é retornado nos acessos subsequentesconsole.log(timestamp.value); // 2025-01-18T09:00:00.000ZCarregamento Pesado de Configuração
const config = reusable(() => { // Leitura de arquivo, análise de variáveis de ambiente, etc. return loadConfigFromFile();});
// Configuração é carregada apenas quando necessáriofunction processData() { const settings = config.value; // Carregamento executa apenas no primeiro acesso // ...}Como Funciona a Implementação
- Executa a função init no primeiro acesso ao getter
- Sobrescreve value como uma propriedade de dados usando Object.defineProperty
- Nos acessos subsequentes, o getter não é chamado e o valor é retornado diretamente
Assim, o custo de inicialização ocorre apenas no primeiro acesso, e subsequentemente a performance é equivalente ao acesso a uma propriedade normal.
Comparação com Cache de Memória
Diferenças dos caches de memória (como Map):
- Este pattern é especializado para valores únicos
- Não requer gerenciamento de chaves como o Map
- O garbage collection se comporta como objetos normais
Inversamente, use Map ou WeakMap quando desejar cachear múltiplos valores.
Ressalvas
Uma vez inicializado, o valor nunca muda. Para reinicializar, você deve criar uma nova instância reusable. Além disso, se a função init lançar uma exceção, a mesma exceção será relançada nos acessos subsequentes.
hsb.horse