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

مقدمة
بادئ ذي بدء ، ما هو التمديد بشكل عام؟
الامتداد هو سكر نحوي يمتد فصلًا حاليًا في مكان مختلف عن وحدة إعلان الفصل.
في البرمجة ، كانت طرق الإرشاد موجودة منذ وقت طويل ، لذا فقد وصلوا إلى النبال. يستخدم الامتداد بنشاط في لغات مثل C # و Java عبر Manifold و Swift و Kotlin والعديد من اللغات الأخرى.
المشكلة
دعنا نقول أن لدينا طريقة catchError ، والتي هي مجرد فظيعة ويجب إعادة كتابتها إلى وظيفة جديدة رائعة. لنفترض أنه يستخدم دالة من أي نوع كوسيطة ، بدلاً من دالة تمت كتابتها بدقة أو فحص نوع وظيفة ، وذلك لأنه منذ 8 أشهر عند تطوير هذه الوظيفة ، كان ذلك منطقيًا في ذلك الوقت.
أول ما يتبادر إلى الذهن هو إعادة كتابة هذه الوظيفة ، لكننا هنا نواجه مشكلة حدوثها في كثير من الأحيان في المشروع ، وتغيير الوظيفة سيؤدي إلى عدم تشغيل المشروع بأكمله.
حسنا ، إذا كان الخيار الأول ليس بالنسبة لنا. مناسب ، لأسباب منطقية ، ثم يمكنني تنفيذ وظيفة Future جديدة تلبي جميع متطلباتي.
abstract class Future<T> { ... /// Catches any [error] of type [E]. Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(... - ...); } ... }
وسأدعوها مثل هذا:
Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...);
لسوء الحظ ، لا يمكنني إضافة هذه الوظيفة إلى فئة المستقبل. إذا قمت بذلك ، فسوف أضفه أيضًا إلى واجهة Future ، وأي فئة أخرى تنفذ هذه الواجهة ستكون غير مكتملة ولن يتم تجميعها بعد الآن.
حسنًا ، هناك خيار آخر يتمثل في تنفيذ وظيفة طرف ثالث تبدو كما يلي:
Future<T> onFutureError<T, E>(Future<T> source, FutureOr<T> handleError(E error, StackTrace stack)) => source.catchError(... - ...);
ودعوتها تبدو مثل هذا:
Future<String> someString = ...; onFutureError(someString, (FormatException e, s) => ...).then(...);
عظيم ، كل شيء يعمل! لكن من المحزن أن بدأت القراءة بفظاعة. نحن نستخدم الأساليب. والتي يتم تنفيذها داخل الفصل الدراسي ، بحيث يطلق عليها -DoingSomething () ؛ هذا الرمز مفهوم ، لقد قرأته من اليسار إلى اليمين وأقف في رأسي سلسلة من الأحداث. باستخدام وظيفة المساعد يجعل رمز مرهقة وأقل قراءة.
حسنًا ، يمكنني تنفيذ فصل جديد والسماح للمستخدمين بالالتفاف على واجهتهم القديمة مع تحسين الأداء الوظيفي.
class CustomFuture<T> { CustomFuture(Future<T> future) : _wrapper = future; Future<T> _wrapper; Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => _wrapper.catchError(...- ...); }
وستظهر المكالمة كما يلي:
Future<String> someString = ...; CustomFuture(someString).onError((FormatException e, s) => ...).then(...);
تبدو رائعة!
حل مشكلة مع التمديد
بمجرد إيقاف البرمجة في pascal والعودة إلى 2019 ، سيتم تقليل تنفيذ هذه الوظيفة إلى هذا الحجم:
extension CustomFuture <T> on Future<T> { Future<T> onError<E>( FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(...something clever...); }
وهذا ما ستبدو عليه المكالمة:
Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...);
هذا كل شئ! استغرق حل هذه المشكلة فقط 5 أسطر من التعليمات البرمجية. لك. قد تتساءل عن نوع السحر وكيف يعمل.
في الواقع ، يتصرف بالطريقة نفسها التي تعمل بها فئة غلاف ، على الرغم من أنها في الواقع مجرد وظيفة مساعدة
ثابتة . التمديد يسمح لك بالتخلي عن كتابة المجمع الصريح.
هذه ليست غلاف
يعمل تصميم الملحق بطريقة تبدو وكأنها إعلان لفئة حالية ، لكنه يتصرف كما لو كان غلافًا يحتوي على غلاف خاص. ولكن هناك ميزة واحدة مقارنة بفئة الالتفاف ، وهي الوصول إلى الفصل نفسه مباشرة ، بدلاً من الوصول إلى فئة المجمع _wrapper.
لم يتم إنشاء هذه الميزة من أجل الميزات ، ولكن كما قلت سابقًا ، فإن الإضافات هي بالفعل طريقة أكثر ملاءمة لاستدعاء الوظائف الثابتة. هذا يعني أنه لا يوجد كائن مجمّع.
كل شيء ثابت
قلت "طرق التمديد الثابت" أعلاه ، وفعلت ذلك لسبب ما!
وثبة مكتوبة بشكل ثابت. يعرف المترجم نوع كل تعبير في وقت التحويل البرمجي ، لذلك إذا كتبت user.age (19) وكان العمر امتدادًا ، فيجب على المحول البرمجي اكتشاف نوع الملفوفة في الكائن المحدد للعثور على نوع المكالمة بالكامل.
ما هي المشاكل التي يمكن أن تنشأ؟
أبسط مثال على مشاكل الامتداد هو عندما يكون لديك أكثر من امتداد في نطاقه. بشكل أساسي ، يكون الفائز هو الأقرب إلى نوع التعبير الفعلي الذي تتصل به العضو ، مع بعض التحفظات.
تتمثل أسهل طريقة لحل المشكلة في توصيل الامتداد الذي تحتاجه بدقة ، أو يمكنك استخدام الامتداد بشكل صريح:
... List list = ...; MyList(list).printlist(); SomeList(list).printlist(); ... extension MyList on List { void printlist() { print(...- ...); } } extension SomeList on List { void printlist() { print(...- ...); } }
النتائج
- تحتوي لغة السهام على أداة مناسبة لتوسيع الوظائف الحالية.
- يمكنك تمديد الطرق والمشغلين وعمال التسوية والحروف ، ولكن ليس الحقول.
- يمكنك استدعاء طرق الامتداد بشكل صريح أو - عندما لا يكون هناك تعارض مع أحد أعضاء الواجهة أو أي امتداد آخر ضمنيًا.
- تعمل المكالمات الضمنية تمامًا مثل المكالمات الصريحة.
- ملحقات ثابتة. يتم حل كل شيء عنهم على أساس أنواع ثابتة.
إذا فشل إخراج الملحق بسبب تعارضات التمديدات ، فيمكنك القيام بأي مما يلي:
- تطبيق التمديد بشكل صريح.
- قم باستيراد الامتداد المتعارض مع البادئة ، لأنه غير متاح للمكالمة الضمنية.
- لا تقم باستيراد امتداد متعارض على الإطلاق.
هذا كل شئ! يمكنك استخدام التمديد إلى أقصى إمكاناتها.
وبالطبع ، روابط مفيدة:
رفرفة الموقعدارت الموقعأين يمكنني قراءة المزيد عن التمديدقناة برقية حيث أتحدث عن الأحدث في عالم الرفرفة وليس فقط