تم طرح عدة مرات سؤال من الزملاء بأنه ليس من الواضح سبب الحاجة عمومًا إلى المزج (الشوائب) في لغة Dart. قررت أن أرى ما هو موجود على الإنترنت في هذه القضية. إلى حد كبير من غضب المقالات التي يمكن العثور عليها ، يتحدثون بشكل أساسي عن كيفية استخدام الشوائب ، لكنهم لا يفسرون سبب الحاجة إليها ، وفي هذه الحالات يكون استخدامها أفضل من الميراث العادي أو تنفيذ الواجهات. هذه المقالة محاولة لسد هذه الفجوة.
على الرغم من أن هناك مقالات كافية على الإنترنت حول موضوع الشوائب في Dart و Flutter ، إلا أنها لا تضفي الوضوح في رأيي لأن الأمثلة المقدمة توضح الآليات الخالصة لبناء الطبقات مع الشوائب ، وهو أمر بعيد عن المعقول وبالتالي لا يوضح النطاق الحقيقي لتطبيقها . على وجه الخصوص ، التقيت هذا المثال . لدينا:
class Animal {} class Dog {} class Cat {}
ولسبب ما أردنا الحصول على حيوان له خصائص القطط والكلاب في نفس الوقت. في هذه الحالة ، يمكننا القيام بذلك:
class CatDog extends Animal with Cat, Dog {}
هناك سؤالان على الأقل لهذا المثال:
- لماذا نحتاج إلى تقاطع بين القطة والكلب؟
- لماذا لا ترث القط والكلب من
Animal
؟ أليسوا حيوانات؟
في الوقت نفسه ، لماذا لا تزال هناك حاجة إلى الشوائب؟
في رأيي المتواضع ، من أجل فهم معنى الشوائب ، من الضروري البدء في النظر في هذه القضية مع وجود علاقة بالميراث. النقطة الرئيسية في الميراث في OOP هي أن كيان واحد هو تباين لكيان آخر. على سبيل المثال ،
هو شكل من
أو
هو شكل من أشكال
. وهذا هو بالضبط ما يجب أن يكون العامل الحاسم في بناء التسلسل الهرمي للفئة.
إذا نظرنا إلى الميراث من وجهة نظر مختلفة ، فسوف نرى أن
يرث خصائص
، وأن
ترث خصائص
. إذا لم تهتم بالمنطق ، فقد ترغب في وراثة خصائص عدة كيانات مختلفة تقنيًا بحتًا. للقيام بذلك ، تدعم بعض لغات البرمجة الوراثة المتعددة .
يتم انتقاد الوراثة المتعددة لعدد من أوجه القصور (انظر ويكيبيديا ) ، لذلك لا تستخدم العديد من لغات البرمجة الوراثة المتعددة على الإطلاق ، ولكن تستخدم آلية لتنفيذ واجهات و / أو الشوائب. ومن وجهة نظر المنطق ، ليس من السهل فهم الإنشاءات الناتجة عن الميراث المتعدد.
لفهم المواد التالية ، من الضروري أن نتذكر بعض المفاهيم من المنطق الأولي. على وجه الخصوص ، مفاهيم الخصائص الأساسية وغير الأساسية . الخصائص الأساسية للكائنات هي تلك الناتجة عن وجودها والتي تشير إلى فئة معينة من الكائنات. الخصائص غير الأساسية لكائن هي تلك التي لا يؤثر وجودها أو غيابها أو قيم محددة على الكائن الذي ينتمي إلى فئة معينة من الكائنات. على سبيل المثال ، شكل المستطيل هو خاصية أساسية لهذا الشكل ، لأنه إذا قمنا بتغيير هذا الشكل (إزالة أو إضافة جانب أو تغيير الزوايا) ، فسيتوقف المستطيل عن أن يكون مستطيلًا. ولكن إذا قمت بتغيير حجم المستطيل ، فسيظل مستطيلًا. لذلك ، الأبعاد هي خاصية ضئيلة.
يعتمد بناء تسلسل هرمي للفصل عادةً على إضافة أي خصائص أساسية إلى الفئة الأصل. على سبيل المثال
abstract class Shape { void draw(); } class Rectangle extends Shape { @override void draw() { print('Draw rectangle'); } } class Circle extends Shape { @override void draw() { print('Draw circle'); } }
أساس هذا التسلسل الهرمي هو خاصية أساسية لشكل الشكل.
مثال آخر:
abstract class Widget { void render(); } class Container extends Widget { @override void render() { print('Renders container'); } } class Text extends Widget { @override void render('Render text'); }
خاصية أساسية هنا هو الغرض من القطعة.
لنفترض الآن أننا نحتاج إلى إضافة بعض الممتلكات غير الضرورية إلى كياناتنا. مثل هذه الخاصية ، على سبيل المثال ، هو اللون. دعونا الآن نريد تلوين بعض الأشكال والحاجيات.
للقيام بذلك ، يمكنك بالطبع استخدام الميراث وتطبيق PaintableShape
و PaintableWidget
. لكن هذا ليس ملائمًا ، لأنه أولاً ، سيتعين علينا تكرار تنفيذ وظيفة التلوين في كلا التسلسلين الهرميين ، وثانياً ، بالنسبة لكل شخصية والقطعة التي نريد تلوينها ، سيتعين علينا تنفيذ فئات جديدة ، على سبيل المثال ، PaintableRect
و PaintableContainer
.
يمكنك استخدام الآلية لتنفيذ واجهات. ثم نحصل على شيء مثل هذا:
enum Color {red, yellow, green} abstract class Paintable { void paint(Color color); Color get color; } class PaintableRect extends Rectangle implements Paintable { Color _color; @override void paint(Color color) {_color = color;} @override Color get color => _color; } class PaintableContainer extends Container implements Paintable { Color _color; @override void paint(Color color) {_color = color;} @override Color get color => _color; }
كما ترون ، هذا ليس هو الحل الأفضل أيضًا ، حيث يتعين علينا تكرار نفس الكود لكل كيان قابل للحل.
ولكن يمكن حل كل هذه المشكلات إذا تمت إزالة الوظيفة المرتبطة بخاصية ضئيلة كإضافة منفصلة (mixin):
enum Color {red, yellow, green} mixin PaintableMixin { Color _color; void paint(Color color) {_color = color;} Color get color => _color; } class PaintableRect extends Rectangle with PaintableMixin { @override void draw() { print('Draw rectangle with color $color'); } } class PaintableContainer extends Container with PaintableMixin { @override void render() { print('Render container with color $color'); } }
الآن يمكنك استخدامه:
main() { PaintableRect() ..paint(Color.red) ..draw(); PaintableContainer() ..paint(Color.yellow) ..render(); }
لتلخيص ما سبق ، يمكن للمرء أن يحدد بالطريقة التالية عندما يكون مناسبًا لاستخدام الشوائب: إذا كان هناك العديد من التسلسلات الهرمية المختلفة التي تحتاج إلى إضافة نفس الوظيفية التي تحدد بعض الممتلكات غير الضرورية لكيانات هذه التسلسلات الهرمية. أو يمكن أن يكون تسلسل هرمي واحد ، لكننا نتعامل مع فروعه المختلفة. على سبيل المثال ، ضع في الاعتبار عناصر واجهة تعامل Flutter.
لنفترض أننا نحتاج إلى إضافة وظائف متعلقة بنفس الخاصية إلى بعض عناصر واجهة المستخدم. يتم إنشاء الحاجيات في الرفرفة على النحو التالي:
class MyStatelessWidget extends StatelessWidget {}
أو
class MyStatefulWidget extends StatefulWidget {}
لإضافة خاصية من خلال الميراث ، سيتعين عليك تنفيذ فئتين على الأقل:
class StatelessWidgetWithProperty extends StatelessWidget {} class StatefulWidgetWithPropery extends StatefulWidget {}
في الوقت نفسه ، كما ترون مرة أخرى ، يجب عليك تكرار الوظيفة المرتبطة بالخاصية المضافة.
عند استخدام الشوائب ، يتم حل المشكلة:
mixin Property {} class MyStatelessWidget extends StatelessWidget with Propery {} class MyStatefulWidget extends StatefulWidget with Property {}
بالنسبة لأولئك الذين هم على دراية بأنماط التصميم ، فإن استخدام الشوائب في بعض الحالات يمكن أن يحل محل استخدام نمط الجسر .
في الختام ، تجدر الإشارة إلى أنه بهذه الطريقة يمكن للمرء أن يختلط في وظائف عدة خصائص مختلفة دفعة واحدة في مجموعات عشوائية.
هذه المادة لا تهدف إلى تحديد شامل لاستخدام الشوائب. ربما سيكون بمقدور عقل الاستفسار من المطور العثور على العديد من الاستخدامات الأكثر جمالا لهم. سأكون سعيدًا إذا ظهرت هذه الخيارات لاستخدام الشوائب في التعليقات على هذه المقالة.