النهج الوظيفي لمعالجة الأخطاء في دارت

في الانتقال إلى تكنولوجيا جديدة ، نفقد الأدوات المعتادة للتنمية. في بعض الحالات ، نضطر إلى تحمل مع غيابهم بسبب بعض القيود التقنية ، ولكن إذا أمكن فإننا نحمل الأدوات معنا. عند تطوير تطبيقات أندرويد ، أخذت مثالاً على بنية نظيفة اقترحها فرناندو سيخاس كأساس. فهم أنماط التصميم المستخدمة في Flutter ، قررت التخلي عن هذه البنية لصالح BLoC. لقد اعتدت بسرعة على هذا القالب ، فهو يشبه إلى حد كبير MVVM الذي عملت معه سابقًا ، لكنني لم أرغب في طرح تفاصيل واحدة. عند استدعاء أساليب مستودع التخزين ، كان عليّ أن أتبع بعض الاستثناءات ، وأطرحها على نوع ما ، ووفقًا للنوع ، أنشئ الحالة اللازمة. في رأيي ، فإن هذا الأمر يكتنف المجموعة وقمت بنقل النوع الذي تم استخدامه سابقًا في مشاريع Android استنادًا إلى فرناندو.


إما جاء من لغات البرمجة الوظيفية. يوفر قيمة أحد الأنواع الممكنة:


  • اليسار (في حالة الفشل) ؛
  • صحيح (إذا نجح).

التنفيذ الأساسي لأي منهما
/// Signature of callbacks that have no arguments and return right or left value. typedef Callback<T> = void Function(T value); /// Represents a value of one of two possible types (a disjoint union). /// Instances of [Either] are either an instance of [Left] or [Right]. /// FP Convention dictates that: /// [Left] is used for "failure". /// [Right] is used for "success". abstract class Either<L, R> { Either() { if (!isLeft && !isRight) throw Exception('The ether should be heir Left or Right.'); } /// Represents the left side of [Either] class which by convention is a "Failure". bool get isLeft => this is Left<L, R>; /// Represents the right side of [Either] class which by convention is a "Success" bool get isRight => this is Right<L, R>; L get left { if (this is Left<L, R>) return (this as Left<L, R>).value; else throw Exception('Illegal use. You should check isLeft() before calling '); } R get right { if (this is Right<L, R>) return (this as Right<L, R>).value; else throw Exception('Illegal use. You should check isRight() before calling'); } void either(Callback<L> fnL, Callback<R> fnR) { if (isLeft) { final left = this as Left<L, R>; fnL(left.value); } if (isRight) { final right = this as Right<L, R>; fnR(right.value); } } } class Left<L, R> extends Either<L, R> { final L value; Left(this.value); } class Right<L, R> extends Either<L, R> { final R value; Right(this.value); } 

يعد التنفيذ أساسيًا للغاية ، وهو أدنى مستوى من الحلول بلغات أخرى ، ولكنه يتكيف مع مهمته. أستخدم هذا النوع كنتيجة لجميع أساليب المستودع ، ويتم نقل معالجة الاستثناء إلى طبقة البيانات. هذا يلغي الحاجة إلى بنيات try / catch ، مما يجعل الكود أكثر قابلية للقراءة.


جرب / أمسك مثال
 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); } 

مثال مع أي منهما
 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); } 

فيما يتعلق بقابلية القراءة ، قد يجادل شخص ما. ربما اعتاد شخص ما على المحاولة / الصيد وسيكون على صواب بطريقته الخاصة ، وهذا في معظم الأحيان هو النكهة. ميزة إضافية هي أننا نستطيع تحديد التسلسل الهرمي للفشل والعودة على الجانب الأيسر. دعنا نجعل فشل مجردة ، وجعلها مشتركة لجميع ميزات ServerFailure ، NetworkFailure وبعض ميزات ContactFailure المحددة للميزة الحالية ، مع الورثة. في الكتلة ، سنعرف بالضبط أي من الإخفاقات المتوقعة.


الجانب السلبي لتنفيذ الفشل على دارت هو عدم وجود فصول مختومة كما في kotlin ، وإلا لن يكون هناك أي ifs مع الصب. اللغة شابة وتتطور بفعالية وآمل أن يأتي الوقت وستكون لدينا أدوات لكتابة معالجات أكثر إيجازاً.


قد لا يعجب أحد الأشخاص بهذا التطبيق ، وقد يجد أنه لا معنى له ، لكنني أردت فقط أن أطلعكم على إمكانية اتباع نهج وظيفي لمعالجة الأخطاء في Dart ، على الرغم من أن الاستخدام لم يكن أنيقًا كما هو الحال في اللغات الأخرى.


الموارد:


شفرة المصدر

Source: https://habr.com/ru/post/ar459757/


All Articles