He estado leyendo mucho sobre el patrón singleton y cómo es "malo" porque hace que las clases lo usen difícil de probar, por lo que debe evitarse. He leído algunos artículos que explican cómo se puede reemplazar el singleton con inyección de dependencia, pero me parece innecesariamente complejo.
Aquí está mi problema con un poco más de detalle. Estoy creando una aplicación móvil usando React Native y quiero hacer un cliente REST que se comunique con el servidor, obtenga datos, publique datos y maneje el inicio de sesión (almacene el token de inicio de sesión y envíelo con cada solicitud después de iniciar sesión).
Mi plan inicial era crear un objeto singleton (RESTClient) que mi aplicación usará inicialmente para iniciar sesión y luego hacer una solicitud enviando las credenciales donde sea necesario. El enfoque DI me parece realmente complicado (tal vez porque nunca usé DI antes) pero estoy usando este proyecto para aprender tanto como pueda, así que quiero hacer lo mejor aquí. Cualquier sugerencia y comentario son muy apreciados.
Editar: ahora me di cuenta de que había formulado mal mi pregunta. Quería alguna guía sobre cómo evitar el patrón singleton en RN y debería incluso hacerlo. Afortunadamente, Samuel me dio el tipo de respuesta que quería. Mi problema era que quería evitar el patrón singleton y usar DI, pero parecía realmente complicado implementarlo en React Native. Investigué un poco más y lo implementé usando el sistema de contexto Reacts.
Para cualquier persona interesada, así es como lo hice. Como dije, utilicé el contexto en RN, que es algo así como accesorios, pero se propaga a cada componente.
En el componente raíz proporciono las dependencias necesarias como esta:
export default class Root extends Component {
getChildContext() {
restClient: new MyRestClient();
}
render() {...}
}
Root.childContextTypes = {restClient: PropTypes.object};
Ahora restClient está disponible en todos los componentes debajo de Root. Puedo acceder así.
export default class Child extends Component {
useRestClient() {
this.context.restClient.getData(...);
}
render() {...}
}
Child.contextTypes = {restClient: PropTypes.object}
Esto efectivamente aleja la creación de objetos de la lógica y desacopla la implementación del cliente REST de mis componentes.
fuente
Respuestas:
La inyección de dependencia no necesita ser compleja en absoluto, y vale la pena aprenderla y usarla. Por lo general, es complicado por el uso de marcos de inyección de dependencia, pero no son necesarios.
En su forma más simple, la inyección de dependencias es pasar dependencias en lugar de importarlas o construirlas. Esto puede ser implementado simplemente usando un parámetro para lo que se importaría. Digamos que tiene un componente llamado
MyList
que necesita usarRESTClient
para obtener algunos datos y mostrarlos al usuario. El enfoque "singleton" se vería así:Esto se ajusta estrechamente
MyList
arestClient
, y no hay forma de que pueda realizar pruebas unitariasMyList
sin pruebasrestClient
. El enfoque DI se vería así:Eso es todo lo que se necesita para usar DI. Agrega como máximo dos líneas de código y puede eliminar una importación. La razón por la que introduje una nueva función "fábrica" es porque AFAIK no puede pasar parámetros de constructor adicionales en React, y prefiero no pasar estas cosas a través de las propiedades React porque no es portátil y todos los componentes principales deben saber pasar todos accesorios para niños.
Entonces, ahora tiene una función para construir
MyList
componentes, pero ¿cómo la usa? El patrón DI burbujea en la cadena de dependencia. Digamos que tiene un componenteMyApp
que usaMyList
. El enfoque "singleton" sería:El enfoque DI es:
Ahora podemos probar
MyApp
sin probarMyList
directamente. Incluso podríamos reutilizarMyApp
con un tipo de lista completamente diferente. Este patrón burbujea hasta la raíz de la composición . Aquí es donde llama a sus fábricas y conecta todos los componentes.Ahora nuestro sistema usa una sola instancia de
RESTClient
, pero lo hemos diseñado de tal forma que los componentes están acoplados de forma flexible y son fáciles de probar.fuente
MyAppFactory
devolver laMyApp
clase?Según sus premisas (aprendizaje), la respuesta más simple es no, los singletons no son la mejor alternativa a la inyección de dependencia .
Si el objetivo es el aprendizaje, encontrará que DI es un recurso más valioso en su caja de herramientas que Singleton. Puede parecer complejo, pero la curva de aprendizaje está en casi todo lo que tienes que aprender desde cero. No te recuestes en tu zona de confort o no habrá aprendizaje en absoluto.
Técnicamente, hay poca diferencia entre singleton y instancia única (lo que creo que estás tratando de hacer). Al aprender DI, se dará cuenta de que puede inyectar instancias únicas en todo el código. Encontrará que su código es más fácil de probar y está acoplado libremente. ( Para más detalles mira la respuesta de Samuel )
Pero no te detengas aquí. Implemente el mismo código con Singleton. Luego compare ambos enfoques.
Comprender y familiarizarse con ambas implementaciones le permitirá saber cuándo son apropiadas y probablemente estará en condiciones de contestarse la pregunta.
Es ahora, durante el entrenamiento, cuando forjas tus Golden Hammers , por lo que si decides evitar aprender DI, es probable que termines implementando singletons cada vez que necesites DI.
fuente
Estoy de acuerdo con las otras respuestas de que aprender qué es DI y cómo usarlo es una buena idea.
Dicho esto, la advertencia de que los singletons hacen que las pruebas sean demasiado difíciles generalmente la hacen personas que usan lenguajes estáticamente escritos (C ++, C #, Java, etc.) .
Por el contrario, en un lenguaje dinámico (Javascript, PHP, Python, Ruby, etc.) , por lo general, es difícil reemplazar un singleton con una implementación específica de prueba de lo que sería en el caso de usar DI.
En ese caso, recomiendo usar el diseño que le parezca más natural a usted y a sus desarrolladores, porque eso tenderá a evitar errores. Si eso resulta en singletons, que así sea.
(Pero, de nuevo: aprenda DI antes de tomar esa decisión).
fuente