React Native - ¿Usar un singleton es la mejor alternativa para DI?


8

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.


3
Considere cambiar el título de la pregunta. En este momento REST y el cliente son elementos irrelevantes. La única pregunta aquí es: ¿Singleton es la mejor alternativa para DI en mi situación específica? .
Laiv

No sé mucho sobre reaccionar, pero en general DI es un concepto simple. Si lo encuentra complejo, sospecho que no ha leído / escuchado una buena explicación.
Ben Aaronson

Respuestas:


15

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 MyListque necesita usar RESTClientpara obtener algunos datos y mostrarlos al usuario. El enfoque "singleton" se vería así:

import restClient from '...' // import singleton

class MyList extends React.Component {
  // use restClient and render stuff
}

Esto se ajusta estrechamente MyLista restClient, y no hay forma de que pueda realizar pruebas unitarias MyListsin pruebas restClient. El enfoque DI se vería así:

function MyListFactory(restClient) {
  class MyList extends React.Component {
    // use restClient and render stuff
  }

  return MyList
}

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 MyListcomponentes, pero ¿cómo la usa? El patrón DI burbujea en la cadena de dependencia. Digamos que tiene un componente MyAppque usa MyList. El enfoque "singleton" sería:

import MyList from '...'
class MyApp extends React.Component {
  render() {
    return <MyList />
  }
}

El enfoque DI es:

function MyAppFactory(ListComponent) {
  class MyApp extends React.Component {
    render() {
      return <ListComponent />
    }
  }

  return MyApp
}

Ahora podemos probar MyAppsin probar MyListdirectamente. Incluso podríamos reutilizar MyAppcon 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.

import RESTClient from '...'
import MyListFactory from '...'
import MyAppFactory from '...'

const restClient = new RESTClient(...)
const MyList = MyListFactory(restClient)
const MyApp = MyAppFactory(MyList)

ReactDOM.render(<MyApp />, document.getElementById('app'))

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.


Si no le importa, me gustaría hacer referencia a su respuesta desde la mía, porque creo que ilustra muy bien los puntos que solo he mencionado superficialmente.
Laiv

2
@Laiv, creo que el término "DI del hombre pobre" está pasando de moda, a favor de "DI pura". Aunque no fue intencionado, el primero suena demasiado negativo ya que implica que los contenedores de IoC son "más ricos".
David Arno

Esta. Pensé que la inyección de dependencia era extremadamente complicada cuando la vi por primera vez, pero después de un tiempo es extremadamente fácil de entender. ¡Y @Samuel lo explicó bien!
Rhys Johns

1
@Samuel Entonces, para aplicar correctamente la DI en React, ¿necesitaría crear fábricas para cada componente? También en su ejemplo, ¿no debería MyAppFactorydevolver la MyAppclase?
Mateo Hrastnik

@MattHammond Los componentes simples que no tienen dependencias no necesitan fábricas. Y así es como lo he hecho, puede haber otros enfoques para DI en React que sean mejores, pero aquí apuntaba a la simplicidad. Gracias por la corrección; Ya lo arreglé.
Samuel

5

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.


0

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).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.