Beim Übergang zu einer neuen Technologie verlieren wir die üblichen Entwicklungswerkzeuge. In einigen Fällen sind wir aufgrund technischer Einschränkungen gezwungen, ihre Abwesenheit in Kauf zu nehmen, aber wenn möglich, tragen wir die Werkzeuge bei uns. Bei der Entwicklung von Android-Anwendungen habe ich die von Fernando Cejas vorgeschlagene reine Architektur als Beispiel genommen . Nachdem ich die in Flutter verwendeten Entwurfsmuster verstanden hatte, entschied ich mich, diese Architektur zugunsten von BLoC aufzugeben. Ich habe mich schnell an diese Vorlage gewöhnt, sie ist der MVVM, mit der ich früher gearbeitet habe, sehr ähnlich, aber ich wollte mich nicht mit einem Detail abfinden. Beim Aufrufen von Repository-Methoden musste ich Ausnahmen abfangen, sie in einen Typ umwandeln und entsprechend dem Typ den erforderlichen Status erstellen. Meiner Meinung nach ist dies sehr unübersichtlich und ich habe den Typ "Beide" portiert, der früher in auf Fernando basierenden Android-Projekten verwendet wurde.
Beide kamen aus funktionalen Programmiersprachen. Es gibt den Wert eines der möglichen Typen an:
- Links (im Fehlerfall);
- Richtig (wenn erfolgreich).
Grundlegende Implementierung von Entweder Die Implementierung ist sehr einfach, schlechter als Lösungen in anderen Sprachen, bewältigt jedoch ihre Aufgabe. Ich verwende diesen Typ als Ergebnis aller Methoden des Repositorys, und die Ausnahmebehandlung wird auf die Datenschicht übertragen. Dadurch entfällt die Notwendigkeit von Try / Catch-Konstrukten, wodurch der Code besser lesbar wird.
Beispiel versuchen / fangen 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); }
Beispiel mit entweder 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); }
In Bezug auf die Lesbarkeit könnte jemand argumentieren. Vielleicht ist jemand daran gewöhnt, es zu versuchen / zu fangen und hat auf seine Weise Recht, zum größten Teil ist dies Aroma. Ein zusätzlicher Vorteil ist, dass wir selbst die Fehlerhierarchie definieren und auf der linken Seite zurückkehren können. Machen wir einen abstrakten Fehler, machen Sie ihn für alle ServerFailure-, NetworkFailure-Funktionen und einige ContactFailure-spezifische Funktionen für die aktuelle Funktion mit Erben gemeinsam. Im Block wissen wir genau, welche Fehler zu erwarten sind.
Der Nachteil bei der Implementierung von Failure on Dart ist das Fehlen versiegelter Klassen wie bei Kotlin, da es sonst keine Wenns beim Casting geben würde. Die Sprache ist jung, entwickelt sich aktiv und ich hoffe, dass die Zeit kommt und wir Werkzeuge haben, um Handler prägnanter zu schreiben.
Jemand mag diese Implementierung vielleicht nicht, findet sie vielleicht sinnlos, aber ich wollte Ihnen nur die Möglichkeit eines funktionalen Ansatzes zur Fehlerbehandlung in Dart vorstellen, obwohl sich die Verwendung nicht als so elegant herausstellte wie in anderen Sprachen.
Ressourcen:
Quellcode