البرمجة المبنية على النموذج الأولي
البرمجة القائمة على النموذج الأولي هي أسلوب من أساليب البرمجة الموجهة للكائنات حيث يتم إعادة استخدام السلوك (المعروف باسم الميراث ) من خلال عملية إعادة استخدام الكائنات الموجودة التي تعمل كنماذج أولية . يمكن أيضًا أن يُعرف هذا النموذج بالبرمجة النموذجية أو الموجهة للنموذج الأولي أو غير القائمة على الفئات أو القائمة على المثيل .
تستخدم البرمجة القائمة على النموذج الأولي الكائنات المعممة للعملية، والتي يمكن بعد ذلك استنساخها وتوسيعها. باستخدام الفاكهة كمثال، سيمثل كائن "الفاكهة" خصائص ووظائف الفاكهة بشكل عام. سيتم استنساخ كائن "الموز" من كائن "الفاكهة" وسيتم إلحاق خصائص عامة خاصة بالموز. سيتم استنساخ كل كائن "موز" فردي من كائن "الموز" العام. قارن ذلك بالنموذج القائم على الفئة ، حيث سيتم توسيع فئة "الفاكهة" بواسطة فئة "الموز" .
كانت أولى لغات البرمجة القائمة على النماذج الأولية هي Director aka Ani (بالإضافة إلى MacLisp ) (1976-1979)، وفي نفس الوقت وليس بشكل مستقل، ThingLab (بالإضافة إلى Smalltalk ) (1977-1981)، ومشاريع الدكتوراه الخاصة بكل من Kenneth Michael Kahn في معهد ماساتشوستس للتكنولوجيا و Alan Hamilton Borning في جامعة ستانفورد (ولكنه يعمل مع Alan Kany في Xerox PARC). قدم Borning كلمة "نموذج أولي" في ورقته البحثية TOPLAS 1981. ربما كانت Yale T Scheme (1981-1984) أول لغة برمجة قائمة على النماذج الأولية مع أكثر من منفذ أو مستخدم، على الرغم من أنها مثل Director و ThingLab في البداية، تتحدث فقط عن الكائنات بدون فئات. كانت اللغة التي جعلت اسم ومفهوم النماذج الأولية شائعين هي Self (1985-1995)، التي طورها David Ungar و Randall Smith للبحث في موضوعات تصميم اللغة الموجهة للكائنات.
منذ أواخر تسعينيات القرن العشرين، أصبح نموذج عدم وجود فئات شائعًا بشكل متزايد. بعض اللغات الحالية الموجهة نحو النماذج الأولية هي JavaScript ( وتطبيقات ECMAScript الأخرى مثل JScript و Flash 's ActionScript 1.0)، و Lua ، و Cecil ، و NewtonScript ، وIo ، و Ioke، و MOO ، و REBOL ، و AHK .
منذ عام 2010، ظهر جيل جديد من اللغات ذات النماذج الأولية الوظيفية البحتة، والتي تقلل من OOP إلى جوهرها: Jsonnet هي لغة وظيفية نقية ديناميكية كسولة مع نظام كائنات نموذج أولي مدمج باستخدام وراثة mixin ؛ Nix هي لغة وظيفية نقية ديناميكية كسولة تبني نظام كائنات مكافئ (Nix "extensions") في تعريفين قصيرين للوظيفة فقط (بالإضافة إلى العديد من وظائف الراحة الأخرى). تُستخدم كلتا اللغتين لتحديد تكوينات البرامج الموزعة الكبيرة (Jsonnet مستوحاة مباشرة من GCL، لغة تكوين Google، التي تحدد بها Google جميع عمليات النشر الخاصة بها، ولها دلالات مماثلة على الرغم من الربط الديناميكي للمتغيرات). منذ ذلك الحين، نفذت لغات أخرى مثل Gerbil Scheme أنظمة نماذج أولية وظيفية كسولة بحتة تستند إلى مبادئ مماثلة.
التصميم والتنفيذ
من الناحية اللغوية، تعني كلمة "نموذج أولي" "الصب الأول" ("صب" بمعنى التصنيع). النموذج الأولي هو شيء ملموس يمكن إنشاء أشياء أخرى منه عن طريق النسخ والتعديل. على سبيل المثال، النموذج الأولي الدولي للكيلوجرام هو كائن فعلي موجود بالفعل، ويمكن إنشاء أشياء جديدة من الكيلوجرام منه عن طريق النسخ. وبالمقارنة، فإن "الفئة" هي شيء مجرد يمكن أن تنتمي إليه الأشياء. على سبيل المثال، جميع الأشياء التي يبلغ وزنها كيلوجرام تنتمي إلى فئة KilogramObject، والتي قد تكون فئة فرعية من MetricObject، وهكذا.
يصف دوغلاس كروكفورد الميراث النموذجي في JavaScript على النحو التالي:
تقوم بإنشاء نماذج أولية لكائنات، ثم ... تقوم بإنشاء مثيلات جديدة. الكائنات قابلة للتغيير في JavaScript، لذا يمكننا زيادة المثيلات الجديدة، وإعطائها حقولًا وطرقًا جديدة. يمكن أن تعمل هذه بعد ذلك كنماذج أولية لكائنات أحدث. لا نحتاج إلى فئات لإنشاء الكثير من الكائنات المتشابهة ... الكائنات ترث من الكائنات. ما الذي قد يكون أكثر توجهاً للكائنات من ذلك؟ [1]
يزعم أنصار البرمجة القائمة على النماذج الأولية أنها تشجع المبرمج على التركيز على سلوك مجموعة معينة من الأمثلة ثم يقلقون لاحقًا بشأن تصنيف هذه الكائنات إلى كائنات نموذجية تُستخدم لاحقًا بطريقة مماثلة للفئات . [2] تشجع العديد من الأنظمة القائمة على النماذج الأولية تغيير النماذج الأولية أثناء وقت التشغيل ، في حين أن عددًا قليلًا جدًا من الأنظمة القائمة على الفئات الموجهة للكائنات (مثل النظام الديناميكي الموجه للكائنات، Common Lisp ، Dylan ، Objective-C ، Perl ، Python ، Ruby ، أو Smalltalk ) تسمح بتغيير الفئات أثناء تنفيذ البرنامج.
تعتمد جميع الأنظمة القائمة على النماذج الأولية تقريبًا على لغات مفسرة ومكتوبة ديناميكيًا . ومع ذلك، فإن الأنظمة القائمة على اللغات المكتوبة بشكل ثابت ممكنة من الناحية الفنية. تعد لغة أوميجا التي تمت مناقشتها في البرمجة القائمة على النماذج الأولية [3] مثالاً على مثل هذا النظام، على الرغم من أن أوميجا ليست ثابتة بشكل حصري وفقًا لموقع أوميجا على الويب، بل إن "مترجمها قد يختار استخدام الربط الثابت حيثما يكون ذلك ممكنًا وقد يحسن من كفاءة البرنامج".
بناء الأشياء
في اللغات التي تعتمد على النماذج الأولية لا توجد فئات صريحة. ترث الكائنات مباشرة من كائنات أخرى من خلال خاصية النموذج الأولي. يتم استدعاء خاصية النموذج الأولي prototypeفي Self و JavaScript ، أو protoفي Io . هناك طريقتان لبناء كائنات جديدة: إنشاء كائن من العدم ("من لا شيء") أو من خلال استنساخ كائن موجود. يتم دعم الطريقة الأولى من خلال شكل من أشكال إعلانات الكائن الحرفي ، حيث يمكن تعريف الكائنات في وقت التشغيل من خلال بناء جملة خاص مثل {...}وتمريرها مباشرة إلى متغير. في حين تدعم معظم الأنظمة مجموعة متنوعة من الاستنساخ، فإن إنشاء كائن من العدم ليس بارزًا. [4]
في اللغات القائمة على الفئات، يتم إنشاء مثيل جديد من خلال دالة إنشاء الفئة ، وهي دالة خاصة تحجز كتلة من الذاكرة لأعضاء الكائن (الخصائص والطرق) وترجع مرجعًا إلى تلك الكتلة. يمكن تمرير مجموعة اختيارية من وسيطات البناء إلى الدالة وعادةً ما يتم الاحتفاظ بها في الخصائص. سوف ترث المثيل الناتج جميع الطرق والخصائص التي تم تعريفها في الفئة، والتي تعمل كنوع من القالب الذي يمكن من خلاله إنشاء كائنات من نفس النوع.
تسمح الأنظمة التي تدعم إنشاء كائنات من العدم بإنشاء كائنات جديدة من الصفر دون استنساخ من نموذج أولي موجود. توفر مثل هذه الأنظمة قواعد نحوية خاصة لتحديد خصائص وسلوكيات الكائنات الجديدة دون الرجوع إلى الكائنات الموجودة. يوجد في العديد من لغات النماذج الأولية كائن جذري، يُسمى غالبًا Object ، والذي يتم تعيينه كنموذج أولي افتراضي لجميع الكائنات الأخرى التي تم إنشاؤها في وقت التشغيل والذي يحمل طرقًا مطلوبة بشكل شائع مثل toString()وظيفة لإرجاع وصف للكائن كسلسلة. أحد الجوانب المفيدة لإنشاء كائن من العدم هو ضمان عدم وجود تعارضات في مساحة أسماء أسماء كائن جديد (الخصائص والطرق) مع كائن Object ذي المستوى الأعلى . (في لغة JavaScript ، يمكن للمرء القيام بذلك باستخدام نموذج أولي فارغ، أي Object.create(null).)
يشير الاستنساخ إلى عملية يتم من خلالها إنشاء كائن جديد عن طريق نسخ سلوك كائن موجود (نموذجه الأولي). يحمل الكائن الجديد بعد ذلك جميع صفات الكائن الأصلي. من هذه النقطة فصاعدًا، يمكن تعديل الكائن الجديد. في بعض الأنظمة، يحتفظ الكائن الفرعي الناتج برابط صريح (عبر التفويض أو التشابه ) مع نموذجه الأولي، وتتسبب التغييرات في النموذج الأولي في ظهور تغييرات مقابلة في استنساخه. لا تنشر أنظمة أخرى، مثل لغة البرمجة Kevo الشبيهة بلغة فورث ، التغيير من النموذج الأولي بهذه الطريقة وتتبع بدلاً من ذلك نموذجًا أكثر تسلسلًا حيث لا تنتشر التغييرات في الكائنات المستنسخة تلقائيًا عبر الأجيال اللاحقة. [2]
// مثال على نمط الميراث النموذجي الحقيقي في JavaScript.
// إنشاء كائن باستخدام تدوين الكائن الحرفي {}.
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".
لمثال آخر:
ثابت foo = { واحد : 1 ، اثنان : 2 };
// bar.[[prototype]] =
foo const bar = Object.create ( foo ) ;
شريط . ثلاثة = 3 ؛
شريط واحد ؛ // 1 شريط اثنان ؛ // 2 شريط ثلاثة ؛ // 3
وفد
في اللغات التي تعتمد على النماذج الأولية والتي تستخدم التفويض ، يكون وقت تشغيل اللغة قادرًا على إرسال الطريقة الصحيحة أو العثور على قطعة البيانات الصحيحة ببساطة عن طريق اتباع سلسلة من مؤشرات التفويض (من الكائن إلى النموذج الأولي الخاص به) حتى يتم العثور على تطابق. كل ما هو مطلوب لإنشاء مشاركة السلوك هذه بين الكائنات هو مؤشر التفويض. على عكس العلاقة بين الفئة والمثيل في اللغات الموجهة للكائنات القائمة على الفئة، فإن العلاقة بين النموذج الأولي وفروعه لا تتطلب أن يكون للكائن الفرعي ذاكرة أو تشابه هيكلي مع النموذج الأولي يتجاوز هذا الارتباط. على هذا النحو، يمكن أن يستمر الكائن الفرعي في التعديل والتغيير بمرور الوقت دون إعادة ترتيب بنية النموذج الأولي المرتبط به كما هو الحال في الأنظمة القائمة على الفئة. من المهم أيضًا ملاحظة أنه ليس فقط البيانات، ولكن أيضًا الطرق يمكن إضافتها أو تغييرها. لهذا السبب، تشير بعض اللغات القائمة على النماذج الأولية إلى كل من البيانات والطرق باسم "فتحات" أو "أعضاء". [ بحاجة لمصدر ]
سلسلة
في النمذجة الأولية المتسلسلة - النهج الذي تنفذه لغة برمجة Kevo - لا توجد مؤشرات أو روابط مرئية للنموذج الأولي الأصلي الذي يتم استنساخ كائن منه. يتم نسخ كائن النموذج الأولي (الأصل) بدلاً من الارتباط به ولا يوجد تفويض. ونتيجة لذلك، لن تنعكس التغييرات التي تطرأ على النموذج الأولي في الكائنات المستنسخة. [5] بالمناسبة، تحقق لغة برمجة Cosmos نفس الشيء من خلال استخدام هياكل البيانات المستمرة . [6]
الفرق المفاهيمي الرئيسي بموجب هذا الترتيب هو أن التغييرات التي يتم إجراؤها على كائن النموذج الأولي لا يتم نشرها تلقائيًا إلى النسخ. يمكن اعتبار هذا ميزة أو عيبًا. (ومع ذلك، توفر Kevo بدائيات إضافية لنشر التغييرات عبر مجموعات من الكائنات بناءً على تشابهها - ما يسمى بالتشابه العائلي أو آلية عائلة النسخ [5] - بدلاً من الأصل التصنيفي، كما هو معتاد في نموذج التفويض.) كما يُزعم أحيانًا أن النمذجة الأولية القائمة على التفويض لها عيب إضافي يتمثل في أن التغييرات التي تطرأ على كائن فرعي قد تؤثر على التشغيل اللاحق للكائن الأصلي. ومع ذلك، فإن هذه المشكلة ليست متأصلة في النموذج القائم على التفويض ولا توجد في اللغات القائمة على التفويض مثل JavaScript، والتي تضمن تسجيل التغييرات التي تطرأ على كائن فرعي دائمًا في الكائن الفرعي نفسه وليس في الوالدين أبدًا (أي أن قيمة الطفل تحجب قيمة الوالد بدلاً من تغيير قيمة الوالد).
في التنفيذات البسيطة، سيكون للنمذجة الأولية المتسلسلة بحث أسرع عن الأعضاء من النمذجة الأولية القائمة على التفويض (لأنه لا توجد حاجة لمتابعة سلسلة الكائنات الأصلية)، ولكنها على العكس من ذلك ستستخدم المزيد من الذاكرة (لأن جميع الفتحات يتم نسخها، بدلاً من وجود فتحة واحدة تشير إلى الكائن الأصلي). ومع ذلك، يمكن للتنفيذات الأكثر تطورًا تجنب هذه المشكلة، على الرغم من أن المقايضات بين السرعة والذاكرة مطلوبة. على سبيل المثال، يمكن للأنظمة ذات النمذجة الأولية المتسلسلة استخدام تنفيذ النسخ عند الكتابة للسماح بمشاركة البيانات خلف الكواليس - وتتبع Kevo مثل هذا النهج بالفعل. [7] وعلى العكس من ذلك، يمكن للأنظمة ذات النمذجة الأولية القائمة على التفويض استخدام التخزين المؤقت لتسريع البحث عن البيانات.
نقد
غالبًا ما يكون لدى أنصار نماذج الكائنات القائمة على الفئة الذين ينتقدون الأنظمة القائمة على النماذج الأولية مخاوف مماثلة للمخاوف التي لدى أنصار أنظمة النوع الثابتة للغات البرمجة بشأن أنظمة النوع الديناميكية (انظر نوع البيانات ). وعادةً ما تتضمن هذه المخاوف الصحة والسلامة والقدرة على التنبؤ والكفاءة وعدم إلمام المبرمج.
في النقاط الثلاث الأولى، غالبًا ما يُنظر إلى الفئات على أنها مماثلة للأنواع (في معظم لغات البرمجة الموجهة للكائنات ذات النوع الثابت فإنها تؤدي هذا الدور) ويتم اقتراحها لتوفير ضمانات تعاقدية لمثيلاتها، ولمستخدمي مثيلاتها، بأنها ستتصرف بطريقة معينة.
فيما يتعلق بالكفاءة، فإن إعلان الفئات يبسط العديد من تحسينات المترجم التي تسمح بتطوير طريقة فعالة والبحث عن متغيرات المثيل. بالنسبة للغة Self ، تم قضاء الكثير من وقت التطوير في تطوير وتجميع وتفسير التقنيات لتحسين أداء الأنظمة القائمة على النماذج الأولية مقابل الأنظمة القائمة على الفئات.
من الانتقادات الشائعة الموجهة للغات القائمة على النماذج الأولية أن مجتمع مطوري البرامج غير معتاد عليها، على الرغم من شعبية JavaScript وانتشارها في السوق . ومع ذلك، تتزايد المعرفة بالأنظمة القائمة على النماذج الأولية مع انتشار أطر عمل JavaScript والاستخدام المعقد لـ JavaScript مع نضوج شبكة الويب العالمية . [8] [ بحاجة لمصدر ] قدمت ECMAScript 6 الفئات كحلوى نحوية على الميراث القائم على النماذج الأولية في JavaScript، مما يوفر طريقة بديلة لإنشاء الكائنات وإدارة الميراث. [9]
اللغات التي تدعم البرمجة القائمة على النموذج الأولي
- لغة متزامنة تعتمد على الممثل (ABCL): ABCL/1 ، ABCL/R ، ABCL/R2 ، ABCL/c+
- أغورا
- أوتوهتكي
- سيسيل وديزل من كريج تشامبرز
- كولد سي
- كولا
- اللغة الشائعة
- سماوي
- إيكماسكربت
- ActionScript 1.0، المستخدم بواسطة Adobe Flash و Adobe Flex
- ECMAScript لـ XML (E4X)
- جافا سكريبت
- جيه سكريبت
- تايب سكريبت
- أيو
- ايوكي
- جسونيت
- لوج توك
- ال بي سي
- لوا
- م2000
- القيقب
- موو
- نيكو
- نيوتن سكريبت
- نيم
- نيكس
- لغة ليشپ الكائنية
- مائل
- اوميجا
- أوبن لازلو
- Perl ، مع وحدة Class::Prototyped
- بايثون مع prototype.py.
- R ، مع الحزمة الأولية
- ريبول
- الأحمر (لغة البرمجة)
- روبي (لغة برمجة)
- الذات
- سيف
- سليت (لغة برمجة)
- سمارت فروج
- فرقعة!
- إي تويز
- تادس
- Tcl مع تمديد snit
- أوماجين [10]
انظر أيضا
مراجع
- ^ كروكفورد، دوغلاس. "الوراثة النموذجية في جافا سكريبت" . تم الاسترجاع في 22 يونيو 2021 .
- ^ ab Taivalsaari, Antero (1996). "Section 1.1". Classes vs. Prototypes: Some Philosophical and Historical Observations . ص 44-50. CiteSeerX 10.1.1.56.4713 .
- ^ Blaschek, Günther. "Section 2.8". Omega: statically typed Prototypes . ص 177.
- ^ دوني، كريستوف؛ مالينفان، جاك؛ باردو، دانيال. "القسم 1.2" (PDF) . تصنيف لغات البرمجة القائمة على النماذج الأولية . ص 17.
- ^ من قبل أنتيرو تايفالسار (2009). "تبسيط جافا سكريبت باستخدام وراثة النموذج الأولي القائمة على التجميع" (PDF) . جامعة تامبيري للتكنولوجيا. مؤرشف من الأصل (PDF) في 2011-08-13 . تم الاسترجاع في 2015-03-11 .
نفذت Kevo نموذج كائن قائم على التجميع البحت حيث تم إنشاء كائنات جديدة عن طريق النسخ وكانت مساحات الأسماء لجميع الكائنات دائمًا مكتفية ذاتيًا بالكامل. ... علاوة على ذلك، كان لدى Kevo آلية
عائلة استنساخ
داخلية جعلت من الممكن تتبع "أنساب" التغييرات بين مجموعات الكائنات، بحيث يمكن نشر التغييرات على الكائنات الفردية إلى كائنات أخرى عند الضرورة.
- ^ "البرمجة الموجهة للكائنات". GitHub . تم الاسترجاع في 2023-09-04 .
- ^ تايفالساري، أنتيرو (1992). "كيفو، لغة برمجة كائنية التوجه تعتمد على النموذج الأولي وتعتمد على عمليات التجميع والوحدات النمطية". تقرير تقني، تقرير LACIR 92-02 . جامعة فيكتوريا.
- ^ "البرمجة الشيئية النموذجية باستخدام جافا سكريبت". A List Apart . 2016-04-26 . تم الاسترجاع في 2018-10-21 .
- ^ "الفصول الدراسية". مرجع JavaScript . شبكة مطوري Mozilla . تم الاسترجاع في 9 فبراير 2016 .
- ^ لغة برمجة خاصة. http://www.davidbrebner.com/?p=4 تحتوي على بعض الأمثلة الأساسية للاستخدام.
قراءة إضافية
- العبادي, مارتن ; لوكا كارديلي (1996). نظرية الكائنات . سبرينغر-فيرلاغ. رقم ISBN 978-1-4612-6445-3.
- الحرب الطبقية: الطبقات مقابل النماذج الأولية، بقلم بريان فوت.
- نوبل، جيمس؛ تايفالساري، أنتيرو؛ مور، إيفان، محررون (1999). البرمجة القائمة على النموذج الأولي: المفاهيم واللغات والتطبيقات . دار نشر سبرينغر. رقم ISBN 981-4021-25-3.
- استخدام الكائنات النموذجية لتنفيذ السلوك المشترك في الأنظمة الموجهة للكائنات، بقلم هنري ليبرمان، 1986.
