البرمجة القائمة على النماذج الأولية
البرمجة القائمة على النماذج الأولية هي نمط من أنماط البرمجة الكائنية التوجه، حيث يتم إعادة استخدام السلوك (المعروف بالوراثة ) من خلال عملية إعادة استخدام الكائنات الموجودة التي تعمل كنماذج أولية . يُعرف هذا النموذج أيضاً بالبرمجة النموذجية ، أو البرمجة الموجهة نحو النماذج الأولية، أو البرمجة غير القائمة على الفئات ، أو البرمجة القائمة على الحالات .
تعتمد البرمجة القائمة على النماذج الأولية على معالجة الكائنات المعممة، والتي يمكن استنساخها وتوسيعها. على سبيل المثال، يُمثل كائن "فاكهة" خصائص ووظائف الفاكهة بشكل عام. يُستنسخ كائن "موز" من كائن "فاكهة" وتُضاف إليه خصائص عامة خاصة بالموز. يُستنسخ كل كائن "موز" على حدة من كائن "موز" العام. قارن هذا بالنموذج القائم على الأصناف ، حيث يُوسّع صنف "فاكهة" بواسطة صنف "موز" .
تاريخ
كانت لغة البرمجة KRL التي طورها دانيال جي. بوبرو وتيري وينوغراد أول لغة برمجة قائمة على النموذج الأولي . وبفضل معيار ستانفورد AIM-293 لعام 1976، أصبحت أول لغة تستخدم مصطلح " موجهة للكائنات " بالمعنى الحديث، وهي التي قدمت مصطلح " الوراثة " (الخصائص) بشكل رسمي، ونفذت فعلياً الوراثة المتعددة ، كما أنها أول من قدم مصطلح "النموذج الأولي" بمعناه الحالي.
من بين الأنظمة النموذجية المبكرة الأخرى، نجد Director المعروف أيضًا باسم Ani (المبني على MacLisp ) (1976-1979)، وThingLab (المبني على Smalltalk ) (1977-1981)، وكلاهما مشروعان لنيل درجة الدكتوراه، أحدهما لكينيث مايكل كان في معهد ماساتشوستس للتكنولوجيا (MIT) والآخر لألان هاميلتون بورنينغ في جامعة ستانفورد (بالتعاون مع آلان كاي في مركز أبحاث زيروكس بارك). أضاف فريق Actors التابع لهيويت في معهد ماساتشوستس للتكنولوجيا دعمًا لبرمجة Prototype OO إلى ACT-1 في عام 1979 استنادًا إلى عمل كان، وأطلقوا على الآلية الأساسية اسم "التفويض" لتمييزها عن دعم Class OO الذي أضافوه أيضًا تحت مسمى "الوراثة". وقد شاع استخدام مصطلح "النموذج الأولي" في هذا السياق في بحثه المنشور عام 1981 في مجلة ACM Transactions on Programming Languages and Systems (TOPLAS) . في غضون ذلك، قام ريس وآدامز بتطبيق البرمجة الكائنية التوجه بدون استخدام الأصناف باستخدام الإغلاقات المعجمية في لغة سْكيم من جامعة ييل (1981-1989) - لم يصفوها بأنها "نموذج أولي"، لكنها كانت متماثلة. وفي وقت لاحق، نشر كين ديكي نموذج البرمجة الكائنية التوجه الخاص بهما كشفرة سْكيم قابلة للنقل في عام 1992 تحت اسم YASOS.
اللغة التي ساهمت في انتشار مصطلح ومفهوم النماذج الأولية بين الأكاديميين هي لغة سيلف (1985-1995)، التي طورها ديفيد أونجار وراندال سميث. أما اللغة التي جعلت النماذج الأولية متاحة للجميع فهي لغة جافا سكريبت لبريندان إيتش (1995-مستمرة).
منذ أواخر التسعينيات، ازدادت شعبية نموذج البرمجة غير القائم على الفئات. ومن بين لغات البرمجة الحالية الموجهة نحو النماذج الأولية: جافا سكريبت (وغيرها من تطبيقات ECMAScript مثل JScript و ActionScript 1.0 من Flash )، ولوا ، وسيسيل ، ونيوتن سكريبت ، و Io ، وIoke ، و MOO ، و REBOL ، و AHK .
منذ العقد الثاني من الألفية الثانية، ظهر جيل جديد من لغات البرمجة ذات النماذج الأولية الوظيفية البحتة، والتي تُختزل البرمجة الكائنية التوجه إلى جوهرها: Jsonnet هي لغة ديناميكية وظيفية بحتة كسولة ، مزودة بنظام نماذج أولية مدمج للكائنات باستخدام وراثة المزيج ؛ وNix هي لغة ديناميكية وظيفية بحتة كسولة ، تُنشئ نظام كائنات مكافئًا (امتدادات Nix) في تعريفين قصيرين للدوال فقط (بالإضافة إلى العديد من الدوال المساعدة الأخرى). تُستخدم كلتا اللغتين لتعريف تكوينات برمجية موزعة واسعة النطاق (Jsonnet مستوحاة مباشرة من GCL، لغة تكوين جوجل، التي تُعرّف جوجل من خلالها جميع عمليات النشر الخاصة بها، ولها دلالات مماثلة مع ربط ديناميكي للمتغيرات). ومنذ ذلك الحين، قامت لغات أخرى مثل Gerbil Scheme بتطبيق أنظمة نماذج أولية وظيفية بحتة كسولة، استنادًا إلى مبادئ مماثلة.
التصميم والتنفيذ
من الناحية اللغوية، تعني كلمة "نموذج أولي" "الصبة الأولى" (بمعنى التصنيع). النموذج الأولي هو شيء ملموس، يمكن إنشاء أشياء أخرى منه عن طريق النسخ والتعديل. على سبيل المثال، النموذج الأولي الدولي للكيلوغرام هو شيء حقيقي موجود بالفعل، ويمكن إنشاء كائنات جديدة بالكيلوغرام منه عن طريق النسخ. في المقابل، "الفئة" هي شيء مجرد، يمكن أن تنتمي إليه الكائنات. على سبيل المثال، جميع كائنات الكيلوغرام تنتمي إلى فئة كائن الكيلوغرام، والتي قد تكون فئة فرعية من كائن متري، وهكذا.
يصف دوغلاس كروكفورد مفهوم الوراثة النموذجية في جافا سكريبت على النحو التالي:
تقوم بإنشاء نماذج أولية للكائنات، ثم... تُنشئ نسخًا جديدة منها. الكائنات قابلة للتغيير في جافا سكريبت، لذا يُمكننا إضافة خصائص جديدة إلى النسخ الجديدة، ومنحها حقولًا وأساليب جديدة. يُمكن لهذه النسخ الجديدة أن تعمل كنماذج أولية لكائنات أحدث. لسنا بحاجة إلى فئات لإنشاء العديد من الكائنات المتشابهة... الكائنات ترث من كائنات أخرى. أي شيء يُمكن أن يكون أكثر توجهاً نحو الكائنات من ذلك؟ [ 1 ]
يرى مؤيدو البرمجة القائمة على النماذج الأولية أنها تشجع المبرمج على التركيز على سلوك مجموعة من الأمثلة، ثم الاهتمام لاحقًا بتصنيف هذه الكائنات إلى كائنات نموذجية تُستخدم لاحقًا بطريقة مشابهة للفئات . [ 2 ] تشجع العديد من الأنظمة القائمة على النماذج الأولية على تعديل النماذج الأولية أثناء وقت التشغيل ، بينما لا تسمح سوى قلة قليلة من أنظمة البرمجة الكائنية الموجهة القائمة على الفئات (مثل نظام البرمجة الكائنية الديناميكي، و Common Lisp ، و Dylan ، و Objective-C ، و Perl ، و Python ، و Ruby ، و Smalltalk ) بتعديل الفئات أثناء تنفيذ البرنامج.
تعتمد معظم الأنظمة القائمة على النماذج الأولية على لغات مُفسَّرة وديناميكية الكتابة . مع ذلك، فإن الأنظمة القائمة على لغات ثابتة الكتابة ممكنة من الناحية التقنية. تُعد لغة أوميغا، التي نوقشت في كتاب "البرمجة القائمة على النماذج الأولية " [ 3 ] ، مثالًا على هذا النوع من الأنظمة، مع أن موقع أوميغا الإلكتروني يُشير إلى أنها ليست ثابتة الكتابة بشكل كامل، بل "قد يختار مُترجمها استخدام الربط الثابت حيثما أمكن، مما قد يُحسِّن كفاءة البرنامج".
بناء الكائنات
في اللغات القائمة على النماذج الأولية، لا توجد فئات صريحة. ترث الكائنات مباشرةً من كائنات أخرى عبر خاصية النموذج الأولي. تُسمى خاصية النموذج الأولي prototypeفي لغة Self وجافا سكريبت ، أو protoفي لغة Io . هناك طريقتان لإنشاء كائنات جديدة: إنشاء كائن من العدم ( ex nihilo ) أو استنساخ كائن موجود. تُدعم الطريقة الأولى من خلال نوع من أنواع تعريف الكائنات الحرفية ، حيث يمكن تعريف الكائنات في وقت التشغيل باستخدام صيغة خاصة، {...}وتمريرها مباشرةً إلى متغير. بينما تدعم معظم الأنظمة أنواعًا مختلفة من الاستنساخ، فإن إنشاء الكائنات من العدم ليس شائعًا. [ 4 ]
في لغات البرمجة القائمة على الأصناف، يُنشأ كائن جديد من خلال دالة البناء الخاصة بالصنف ، وهي دالة خاصة تحجز مساحة من الذاكرة لأعضاء الكائن (الخصائص والأساليب) وتعيد مرجعًا إلى تلك المساحة. يمكن تمرير مجموعة اختيارية من وسائط البناء إلى الدالة، وعادةً ما تُخزَّن في الخصائص. سيرث الكائن الناتج جميع الأساليب والخصائص المُعرَّفة في الصنف، الذي يعمل كقالب يُبنى منه كائنات من نفس النوع.
تتيح الأنظمة التي تدعم إنشاء الكائنات من الصفر إنشاء كائنات جديدة دون الحاجة إلى استنساخها من نموذج أولي موجود. توفر هذه الأنظمة صيغة خاصة لتحديد خصائص وسلوكيات الكائنات الجديدة دون الرجوع إلى الكائنات الموجودة. في العديد من لغات النماذج الأولية، يوجد كائن جذري، يُسمى غالبًا Object ، يُعيّن كنموذج أولي افتراضي لجميع الكائنات الأخرى التي تُنشأ أثناء التشغيل، ويحتوي على طرق شائعة الاستخدام، مثل toString()دالة لإرجاع وصف للكائن كسلسلة نصية. من الجوانب المفيدة لإنشاء الكائنات من الصفر ضمان عدم وجود تعارض في أسماء خانات الكائن الجديد (الخصائص والطرق) مع اسم الكائن Object ذي المستوى الأعلى . (في لغة جافا سكريبت ، يمكن تحقيق ذلك باستخدام نموذج أولي فارغ Object.create(null)).
يشير الاستنساخ إلى عملية يتم فيها إنشاء كائن جديد بنسخ سلوك كائن موجود (نموذجه الأولي). يحمل الكائن الجديد جميع خصائص الكائن الأصلي. ومن هذه النقطة فصاعدًا، يمكن تعديل الكائن الجديد. في بعض الأنظمة، يحتفظ الكائن الفرعي الناتج برابط صريح (عبر التفويض أو التشابه ) مع نموذجه الأولي، وتؤدي التغييرات في النموذج الأولي إلى ظهور تغييرات مماثلة في نسخته. أما أنظمة أخرى، مثل لغة البرمجة Kevo الشبيهة بلغة Forth ، فلا تنقل التغييرات من النموذج الأولي بهذه الطريقة، بل تتبع نموذجًا تسلسليًا حيث لا تنتقل التغييرات في الكائنات المستنسخة تلقائيًا إلى الكائنات الفرعية. [ 2 ]
// مثال على أسلوب التوريث النموذجي الحقيقي في جافا سكريبت.// إنشاء الكائن باستخدام صيغة الكائن الحرفي {}. const foo = { name : "foo" , one : 1 , two : 2 };// كائن آخر. const bar = { two : "two" , three : 3 };// Object.setPrototypeOf() هي دالة تم تقديمها في ECMAScript 2015. // ولتبسيط الأمر، لنفترض أن السطر التالي يعمل بغض النظر عن المحرك المستخدم: Object.setPrototypeOf ( bar , foo ); // foo هو الآن النموذج الأولي لـ bar.// إذا حاولنا الوصول إلى خصائص foo من bar من الآن فصاعدًا، // فسوف ننجح. bar.one ; // يتم حلها إلى 1.// يمكن الوصول أيضًا إلى خصائص الكائن الفرعي. bar.three ; // يتم حلها إلى 3.// خصائص النموذج الأولي الظلي للخصائص الخاصة. bar.two ; // يتم حلها إلى "two". bar.name ; // لا تتأثر، يتم حلها إلى "foo". foo.name ; // يتم حلها إلى "foo".مثال آخر:
const foo = { one : 1 , two : 2 };// bar.[[prototype]] = foo const bar = Object . create ( foo );bar . three = 3 ;bar.one ; // 1 bar.two ; // 2 bar.three ; // 3وفد
في اللغات القائمة على النماذج الأولية والتي تستخدم التفويض ، يكون وقت تشغيل اللغة قادرًا على توجيه الدالة الصحيحة أو العثور على البيانات المطلوبة ببساطة عن طريق تتبع سلسلة من مؤشرات التفويض (من الكائن إلى نموذجه الأولي) حتى يتم العثور على تطابق. كل ما هو مطلوب لإنشاء هذه المشاركة السلوكية بين الكائنات هو مؤشر التفويض. على عكس العلاقة بين الفئة والمثيل في لغات البرمجة الكائنية الموجهة القائمة على الفئات، فإن العلاقة بين النموذج الأولي وفروعه لا تتطلب أن يكون للكائن الفرعي تشابه في الذاكرة أو البنية مع النموذج الأولي خارج هذا الرابط. وبالتالي، يمكن تعديل الكائن الفرعي وتغييره بمرور الوقت دون إعادة ترتيب بنية النموذج الأولي المرتبط به كما هو الحال في الأنظمة القائمة على الفئات. من المهم أيضًا ملاحظة أنه لا يمكن إضافة البيانات أو تغييرها فحسب، بل يمكن أيضًا إضافة الدوال أو تغييرها. لهذا السبب، تشير بعض اللغات القائمة على النماذج الأولية إلى كل من البيانات والدوال باسم "الفتحات" أو "الأعضاء".
سلسلة
في النمذجة التجميعية - وهي الطريقة المُطبقة في لغة برمجة Kevo - لا توجد مؤشرات أو روابط مرئية للنموذج الأولي الأصلي الذي يُستنسخ منه الكائن. يُنسخ الكائن الأصلي (الأب) مباشرةً دون ربطه به، ولا يوجد تفويض. ونتيجةً لذلك، لن تنعكس التغييرات التي تُجرى على النموذج الأولي في الكائنات المُستنسخة. [ 5 ] وبالمناسبة، تُحقق لغة برمجة Cosmos نفس النتيجة باستخدام هياكل البيانات المُستمرة . [ 6 ]
يتمثل الاختلاف المفاهيمي الرئيسي في هذا الترتيب في أن التغييرات التي تُجرى على كائن نموذجي لا تنتقل تلقائيًا إلى النسخ المستنسخة. وقد يُنظر إلى هذا على أنه ميزة أو عيب. (مع ذلك، يوفر Kevo أدوات إضافية لنشر التغييرات عبر مجموعات من الكائنات بناءً على تشابهها - ما يُسمى بتشابهات العائلة أو آلية عائلة النسخ المستنسخة [ 5 ] - بدلاً من الأصل التصنيفي، كما هو شائع في نموذج التفويض). يُزعم أحيانًا أن للنمذجة القائمة على التفويض عيبًا إضافيًا يتمثل في أن التغييرات التي تُجرى على كائن فرعي قد تؤثر على عمل الكائن الأصل لاحقًا. ومع ذلك، فإن هذه المشكلة ليست متأصلة في نموذج التفويض، ولا توجد في لغات البرمجة القائمة على التفويض مثل JavaScript، التي تضمن تسجيل التغييرات التي تُجرى على كائن فرعي دائمًا في الكائن الفرعي نفسه وليس في الكائنات الأصلية (أي أن قيمة الكائن الفرعي تُخفي قيمة الكائن الأصل بدلاً من تغييرها).
في التطبيقات البسيطة، يتميز نموذج التجميع الأولي بسرعة بحث أسرع عن العناصر مقارنةً بنموذج التفويض الأولي (لعدم الحاجة إلى تتبع سلسلة الكائنات الأصلية)، ولكنه في المقابل يستهلك ذاكرة أكبر (لأن جميع الخانات تُنسخ، بدلاً من وجود خانة واحدة تشير إلى الكائن الأصلي). مع ذلك، يمكن للتطبيقات الأكثر تطورًا تجنب هذه المشكلة، على الرغم من ضرورة الموازنة بين السرعة والذاكرة. على سبيل المثال، يمكن للأنظمة التي تستخدم نموذج التجميع الأولي الاستفادة من تقنية النسخ عند الكتابة للسماح بمشاركة البيانات في الخلفية - وهذا النهج مُتبع بالفعل في Kevo. [ 7 ] في المقابل، يمكن للأنظمة التي تستخدم نموذج التفويض الأولي استخدام التخزين المؤقت لتسريع البحث عن البيانات.
نقد
غالباً ما يُبدي مؤيدو نماذج الكائنات القائمة على الأصناف ، والذين ينتقدون الأنظمة القائمة على النماذج الأولية، مخاوف مماثلة لتلك التي يُبديها مؤيدو أنظمة الأنواع الثابتة للغات البرمجة تجاه أنظمة الأنواع الديناميكية (انظر نوع البيانات ). وعادةً ما تتعلق هذه المخاوف بالصحة والسلامة وإمكانية التنبؤ والكفاءة وعدم إلمام المبرمجين بها .
فيما يتعلق بالنقاط الثلاث الأولى، غالباً ما يُنظر إلى الفئات على أنها مماثلة للأنواع (في معظم لغات البرمجة الكائنية ذات الكتابة الثابتة، فإنها تؤدي هذا الدور) ويُقترح أن توفر ضمانات تعاقدية لحالاتها، ولمستخدمي حالاتها، بأنها ستتصرف بطريقة معينة.
فيما يتعلق بالكفاءة، يُسهّل تعريف الأصناف العديد من تحسينات المُصرّف ، مما يسمح بتطوير طرق فعّالة والبحث عن متغيرات الكائنات. بالنسبة للغة "سيلف" ، فقد تم تخصيص وقت تطوير كبير لتطوير وتجميع وتفسير تقنيات لتحسين أداء الأنظمة القائمة على النماذج الأولية مقارنةً بالأنظمة القائمة على الأصناف.
من الانتقادات الشائعة الموجهة للغات البرمجية القائمة على النماذج الأولية، قلة إلمام مجتمع مطوري البرمجيات بها، على الرغم من شعبية جافا سكريبت وانتشارها الواسع في السوق . مع ذلك، تتزايد المعرفة بالأنظمة القائمة على النماذج الأولية مع انتشار أطر عمل جافا سكريبت وتزايد استخداماتها المعقدة مع تطور شبكة الإنترنت العالمية . [ 8 ] وقد قدمت ECMAScript 6 الأصناف كحل برمجي مُبسّط لنمط الوراثة القائم على النماذج الأولية في جافا سكريبت، موفرةً بذلك طريقة بديلة لإنشاء الكائنات وإدارة الوراثة. [ 9 ]
اللغات التي تدعم البرمجة القائمة على النماذج الأولية
- لغة التزامن القائمة على الممثل (ABCL): ABCL/1 ، ABCL/R ، ABCL/R2 ، ABCL/c+
- أغورا
- أوتوهوتكي
- سيسيل وديزل من كريج تشامبرز
- كولد سي
- كولا
- Common Lisp ، مع Sheeple أو CommonORBIT أو مكتبات أخرى.
- سماوي
- ECMAScript
- ActionScript 1.0، المستخدم بواسطة Adobe Flash و Adobe Flex
- ECMAScript لـ XML (E4X)
- جافا سكريبت
- جافا سكريبت
- تايب سكريبت
- Haskell ، مع Kiselyov و Lämmel's OOHaskell، أو Gale's Hoop.
- Io
- إيوك
- جيسونيت
- لغة الاتصال
- LPC
- لغة لوا
- M2000
- خشب القيقب
- مو
- نيكو
- نيوتن سكريبت
- نيم
- Nix (حيث تُسمى النماذج الأولية "امتدادات" أو "تخصيصات")
- لغة Object Lisp
- أوبليك
- أوميغا
- أوبن لازلو
- بيرل ، مع وحدة Class::Prototyped
- R ، مع حزمة proto
- ثورة
- أحمر
- روبي
- Scheme ، مع YASOS أو Gerbil-POO أو مكتبات أخرى.
- الذات
- سيف
- لوح
- سمارت فروج
- فرقعة!
- إي تويز
- تادس
- Tcl مع ملحق Snit
- أوماجين [ 10 ]
انظر أيضاً
مراجع
- ↑ كروكفورد، دوغلاس. "الوراثة النموذجية في جافا سكريبت" . تم الاطلاع عليه بتاريخ 22 يونيو 2021 .
- 1 2 تايفالساري، أنترو (1996). "القسم 1.1". الأصناف مقابل النماذج الأولية: بعض الملاحظات الفلسفية والتاريخية . ص 44-50 . CiteSeerX 10.1.1.56.4713 .
- ↑ بلاشيك، غونتر. "القسم 2.8". أوميغا: النماذج الأولية المكتوبة بشكل ثابت . ص 177.
- ↑ دوني، كريستوف؛ مالينفان، جاك؛ باردو، دانيال. "القسم 1.2" (ملف PDF) . تصنيف لغات البرمجة القائمة على النماذج الأولية . ص 17.
- ١ ٢ أنترو تايفالسار (٢٠٠٩). "تبسيط جافا سكريبت باستخدام وراثة النموذج الأولي القائمة على التسلسل" (ملف PDF) . جامعة تامبيري للتكنولوجيا. مؤرشف من الأصل (ملف PDF) بتاريخ ١٣ أغسطس ٢٠١١. تم الاطلاع عليه بتاريخ ١١ مارس ٢٠١٥.
طبقت Kevo نموذج كائنات قائمًا على التسلسل بشكل كامل، حيث يتم إنشاء كائنات جديدة عن طريق النسخ، وتكون مساحات أسماء جميع الكائنات مكتفية ذاتيًا تمامًا. ... علاوة على ذلك، احتوت Kevo على آلية
استنساخ عائلية
داخلية تُمكّن من تتبع "سلسلة النسب" للتغييرات بين مجموعات الكائنات، بحيث يمكن نشر التغييرات التي تطرأ على كائنات فردية إلى كائنات أخرى عند الضرورة.
- ↑ "البرمجة الكائنية التوجه" . جيت هاب . تم الاطلاع عليه بتاريخ 2023-09-04 .
- ↑ تايفالساري، أنترو (1992). "كيفو، لغة برمجة كائنية التوجه قائمة على النماذج الأولية تعتمد على عمليات الربط والوحدات النمطية". تقرير فني LACIR 92-02 . جامعة فيكتوريا.
- ↑ "البرمجة النموذجية للكائنات باستخدام جافا سكريبت" . A List Apart . 2016-04-26 . تم الاطلاع عليه بتاريخ 2018-10-21 .
- ↑ "الفئات" . مرجع جافا سكريبت . شبكة مطوري موزيلا . تم الاطلاع عليه بتاريخ 9 فبراير 2016 .
- ↑ لغة برمجة نصية خاصة. يحتوي الرابط http://www.davidbrebner.com/?p=4 على بعض الأمثلة الأساسية لاستخدامها.
للمزيد من القراءة
- العبادي, مارتن ; لوكا كارديلي (1996). نظرية الكائنات . سبرينغر-فيرلاغ. رقم ISBN 978-1-4612-6445-3.
- حرب الطبقات: الطبقات مقابل النماذج الأولية ، بقلم برايان فوت.
- نوبل، جيمس؛ تايفالساري، أنترو؛ مور، إيفان، محرران. (1999). البرمجة القائمة على النماذج الأولية: المفاهيم واللغات والتطبيقات . سبرينغر-فيرلاغ. ISBN 981-4021-25-3.
- استخدام الكائنات النموذجية لتنفيذ السلوك المشترك في الأنظمة الموجهة للكائنات ، بقلم هنري ليبرمان، 1986.
- البرمجة القائمة على النماذج الأولية
- البرمجة الكائنية التوجه
- نماذج البرمجة
- نظرية الأنواع
