Dans la transition vers une nouvelle technologie, nous perdons les outils habituels de développement. Dans certains cas, nous sommes obligés de supporter leur absence en raison de certaines limitations techniques, mais si possible, nous emportons les outils avec nous. Lors du développement d'applications Android, j'ai pris comme exemple une architecture propre proposée par Fernando Cejas. Comprenant les modèles de conception utilisés dans Flutter, j'ai décidé d'abandonner cette architecture au profit de BLoC. Je me suis rapidement habitué à ce modèle, il est très similaire à la MVVM avec laquelle j'ai travaillé plus tôt, mais je ne voulais pas accepter un détail. Lors de l'appel de méthodes de référentiel, j'ai dû intercepter des exceptions, les convertir en un certain type et, selon le type, créer l'état nécessaire. À mon avis, cela encombre le bloc et j'ai porté le type Either utilisé plus tôt dans les projets Android basés sur Fernando.
Soit provenait de langages de programmation fonctionnels. Il fournit la valeur de l'un des types possibles:
- Gauche (en cas d'échec);
- Bon (en cas de succès).
Implémentation de base de Soit L'implémentation est très basique, inférieure aux solutions dans d'autres langues, mais fait face à sa tâche. J'utilise ce type comme résultat de toutes les méthodes de référentiel et la gestion des exceptions a été déplacée vers la couche de données. Cela élimine le besoin de constructions try / catch, ce qui rend le code plus lisible.
Exemple d'essai / capture class ContactBloc { final ContactRepository contactRepository; ContactBloc(this.contactRepository); @override Stream<ContactState> mapEventToState(ContactEvent event) async* { if (event is GetContactEvent) { yield LoadContactState(); try { var contact = contactRepository.getById(event.id); yield ContactIsShowingState(contact); } on NetworkConnectionException catch (e) { yield NetworkExceptionState(e); } catch (e) { yield UnknownExceptionState(e); } } } } abstract class ContactRepository { Future<Contact>getById(int id); }
Exemple avec soit class ContactBloc { final ContactRepository contactRepository; ContactBloc(this.contactRepository); @override Stream<ContactState> mapEventToState(ContactEvent event) async* { if (event is GetContactEvent) { yield LoadContactState(); final either = contactRepository.getById(event.id); if (either.isRight) { final contact = either.right; yield ContactIsShowingState(contact); } else { final failure = either.left; if (failure is NetworkFailure) yield NetworkFailureState(failure); if (failure is UnknownFailure) yield UnknownFailureState(failure); } } } } abstract class ContactRepository { Future<Either<Failure, Contact>>getById(int id); }
En ce qui concerne la lisibilité, quelqu'un pourrait discuter. Peut-être que quelqu'un est habitué à essayer / attraper et aura raison à sa manière, pour la plupart c'est de l'arôme. Un avantage supplémentaire est que nous pouvons nous-mêmes définir la hiérarchie des échecs et revenir sur le côté gauche. Faisons un échec abstrait, rendons-le commun à toutes les fonctionnalités ServerFailure, NetworkFailure et à certaines fonctionnalités spécifiques de ContactFailure pour la fonctionnalité actuelle, avec les héritiers. Dans le bloc, nous saurons exactement à quel échec s'attendre.
L'inconvénient de l'implémentation de Failure on Dart est le manque de classes scellées comme dans kotlin, sinon il n'y aurait pas de if avec le casting. La langue est jeune, en plein développement et j'espère que le moment sera venu et que nous aurons des outils pour écrire les gestionnaires plus succinctement.
Quelqu'un peut ne pas aimer cette implémentation, peut la trouver inutile, mais je voulais juste vous présenter la possibilité d'une approche fonctionnelle de la gestion des erreurs dans Dart, bien que l'utilisation ne se soit pas révélée aussi élégante que dans d'autres langues.
Ressources:
Code source