Eu tenho lido muito sobre o padrão singleton e como ele é "ruim", porque dificulta o teste das classes, por isso deve ser evitado. Eu li alguns artigos explicando como o singleton poderia ser substituído pela injeção de dependência, mas parece desnecessariamente complexo para mim.
Aqui está o meu problema com mais detalhes. Estou construindo um aplicativo móvel usando o React Native e quero criar um cliente REST que se comunique com o servidor, obtenha dados, publique dados e lide com o logon (armazene o token de logon e envie-o a cada solicitação após o logon).
Meu plano inicial era criar um objeto singleton (RESTClient) que meu aplicativo usaria inicialmente para efetuar login e, em seguida, fazer o pedido enviando as credenciais quando necessário. A abordagem de DI parece realmente complicada para mim (talvez porque nunca usei DI antes), mas estou usando esse projeto para aprender o máximo possível, para que eu queira fazer o melhor aqui. Todas as sugestões e comentários são muito apreciados.
Edit: Agora eu percebi que tenho formulado minha pergunta mal. Eu queria algumas orientações sobre como evitar o padrão singleton no RN e devo fazê-lo. Felizmente, Samuel me deu o tipo de resposta que eu queria. Meu problema era que eu queria evitar o padrão singleton e usar o DI, mas parecia realmente complicado implementá-lo no React Native. Eu fiz algumas pesquisas adicionais e a implementei usando o sistema de contexto Reacts.
Para quem estiver interessado, aqui está como eu fiz. Como eu disse, usei o contexto no RN, que é algo como adereços, mas é propagado para todos os componentes.
No componente raiz, forneço as dependências necessárias como esta:
export default class Root extends Component {
getChildContext() {
restClient: new MyRestClient();
}
render() {...}
}
Root.childContextTypes = {restClient: PropTypes.object};
Agora restClient está disponível em todos os componentes abaixo de Root. Eu posso acessá-lo assim.
export default class Child extends Component {
useRestClient() {
this.context.restClient.getData(...);
}
render() {...}
}
Child.contextTypes = {restClient: PropTypes.object}
Isso efetivamente afasta a criação de objetos da lógica e desacopla a implementação do cliente REST dos meus componentes.
fonte
Respostas:
A injeção de dependência não precisa ser complexa e vale a pena aprender e usar. Geralmente é complicado pelo uso de estruturas de injeção de dependência, mas elas não são necessárias.
Na sua forma mais simples, a injeção de dependência é transmitir dependências em vez de importá-las ou construí-las. Isso pode ser implementado simplesmente usando um parâmetro para o que seria importado. Digamos que você tenha um componente nomeado
MyList
que precisa ser usadoRESTClient
para buscar alguns dados e exibi-los ao usuário. A abordagem "singleton" seria algo como isto:Este firmemente casais
MyList
pararestClient
, e não há nenhuma maneira você pode teste de unidadeMyList
sem testesrestClient
. A abordagem de DI ficaria assim:É tudo o que é necessário para usar o DI. Ele adiciona no máximo duas linhas de código e você elimina uma importação. A razão pela qual eu introduzi uma nova função "factory" é porque o AFAIK não pode passar parâmetros adicionais do construtor no React, e eu prefiro não passar essas coisas pelas propriedades do React porque ele não é portátil e todos os componentes pais devem saber passar todos adereços para crianças.
Então agora você tem uma função para construir
MyList
componentes, mas como você o usa? O padrão de DI borbulha a cadeia de dependência. Digamos que você tenha um componenteMyApp
que useMyList
. A abordagem "singleton" seria:A abordagem de DI é:
Agora podemos testar
MyApp
sem testarMyList
diretamente. Poderíamos até reutilizarMyApp
com um tipo completamente diferente de lista. Esse padrão borbulha até a raiz da composição . É aqui que você liga para suas fábricas e liga todos os componentes.Agora, nosso sistema usa uma única instância de
RESTClient
, mas nós o projetamos de maneira que os componentes sejam fracamente acoplados e fáceis de testar.fonte
MyAppFactory
retornar aMyApp
turma?De acordo com suas premissas (aprendizado), a resposta mais simples é não, os singletons não são a melhor alternativa à injeção de dependência .
Se o objetivo é aprender, você encontrará o DI como um recurso mais valioso na sua caixa de ferramentas do que o Singleton. Pode parecer complexo, mas a curva de aprendizado está quase em tudo o que você precisa aprender do zero. Não se deite na sua zona de conforto ou não haverá aprendizado.
Tecnicamente, há pouca diferença entre Singleton e instância única (o que eu acho que você está tentando fazer). Aprendendo o DI, você perceberá que pode injetar instâncias únicas em todo o código. Você verá que seu código é mais fácil de testar e está pouco acoplado. ( Para mais detalhes, consulte a resposta de Samuel )
Mas não pare por aqui. Implemente o mesmo código com o Singleton. Em seguida, compare as duas abordagens.
Compreender e familiarizar-se com as duas implementações permitirá que você saiba quando elas são apropriadas e provavelmente você estará em posição de responder a si mesmo à pergunta.
Agora, durante o treinamento, você cria seus Golden Hammers ; portanto, se você decidir evitar o DI, é provável que acabe implementando singletons sempre que precisar.
fonte
Concordo com as outras respostas de que aprender o que é DI e como usá-lo é uma boa ideia.
Dito isto, o aviso de que singletons dificultam o teste é geralmente feito por pessoas que usam linguagens de tipo estaticamente (C ++, C #, Java etc.) .
Por outro lado, em uma linguagem dinâmica (Javascript, PHP, Python, Ruby etc.) , geralmente é muito mais difícil substituir um singleton por uma implementação específica de teste do que seria no caso de você usar DI.
Nesse caso, recomendo usar o design mais natural para você e seus co-desenvolvedores, porque isso tenderá a evitar erros. Se isso resultar em singletons, que assim seja.
(Mas, novamente: aprenda o DI antes de tomar essa decisão.)
fonte