اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'الواجهات'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 4 نتائج

  1. تقدّم هذه السلسلة، هياكل البيانات 101، ثلاثة موضوعات: هياكل البيانات Data Structures: سنناقش هياكل البيانات التي يُوفِّرها إطار التجميعات في لغة جافا Java Collections Framework والتي تُختصرُ إلى JCF، وسنتعلم كيفية استخدام بعض هياكل البيانات مثل القوائم والخرائط وسنرى طريقة عملها. تحليل الخوارزميات ِAlgorithms: سنتعرض لتقنياتٍ تساعد على تحليل الشيفرة وعلى التنبؤ بسرعة تنفيذها ومقدار الذاكرة الذي تتطلَّبه. استرجاع المعلومات Information retrieval: سنَستخدِم الموضوعين السابقين: هياكل البيانات والخوارزميات لإنشاء محرك بحثٍ بسيطٍ عبر الإنترنت، وذلك لنستفيد منهما عمليًّا ونجعل التمارين أكثر تشويقًا. وسنناقش تلك الموضوعات وفقًا للترتيب التالي: سنبدأ بالواجهة List، وسنكتب صنفين ينفذ كلٌ منهما تلك الواجهة بطريقة مختلفة، ثم سنوازن بين هذين الصنفين اللذين كتبناهما وبين صنفي الجافا ArrayList وLinkedList. بعد ذلك، سنقدِّم هياكل بيانات شجريّة الشكل، ونبدأ بكتابة شيفرة التطبيق الأول. حيث سيقرأ هذا التطبيق صفحاتٍ من موقع Wikipedia، ثم يُحلِّل محتوياتها ويعطي النتيجة على هيئة شجرة، وفي النهاية سيمر عبر تلك الشجرة بحثًا عن روابطَ ومزايا أخرى. سنَستخدِم تلك الأدوات لاختبار الفرضيّة الشهيرة "الطريق إلى الفلسفة" Getting to Philosophy. سنتطرق للواجهة Map وصنف الجافا HashMap المُنفِّذ لها، ثم سنكتب أصنافًا تُنفِّذ تلك الواجهة باستخدام جدول hash وشجرة بحثٍ ثنائيّة. أخيرًا، سنستخدِم تلك الأصناف وبعض الأصناف الأخرى التي سنتناولها عبر الكتاب لتنفيذ محرك بحث عبر الإنترنت. سيكون هذا المحرك بمنزلة زاحف crawler يبحث عن الصفحات ويقرؤها، كما أنه سيُفهرِس ويُخزِّن محتويات صفحات الإنترنت بهيئةٍ تُمكِّنه من إجراء عملية البحث فيها بكفاءة، كما أنه سيَعمَل مثل مُسترجِع للمعلومات، أي أنه سيَستقبِل استفساراتٍ من المُستخدِم ويعيد النتائج ذات الصلة. ولنبدأ الآن. لماذا هنالك نوعان من الصنف List؟ عندما يبدأ المبرمجون باستخدام إطار عمل جافا للتجميعات، فإنهم عادةً يحتارون أي الصنفين يختارون ArrayList أم LinkedList. فلماذا تُوفِّر جافا تنفيذين implementations للواجهة List؟ وكيف ينبغي الاختيار بينهما؟ سنجيب عن تلك الأسئلة خلال الفصول القليلة القادمة. سنبدأ باستعراض الواجهات والأصناف المُنفِّذَة لها، وسنُقدِّم فكرة البرمجة إلى واجهة. سننفِّذ في التمارين القليلة الأولى أصنافًا مشابهةً للصنفين ArrayList وLinkedList، لكي نتمكَّن من فهم طريقة عملهما، وسنرى أن لكل منهما عيوبًا ومميزاتٍ، فبعض العمليات تكون أسرع وتحتاج إلى مساحة أقل عند استخدام الصنف ArrayList، وبعضها الآخر يكون أسرع وأصغر عند استخدام الصنف LinkedList، وبهذا يمكن القول: إن تحديد الصنف الأفضل لتطبيقٍ معيّنٍ يعتمد على نوعية العمليات الأكثر استخدامًا ضمن ذلك التطبيق. الواجهات في لغة جافا تُحدّد الواجهة بلغة جافا مجموعةً من التوابع methods، ولا بُدّ لأي صنفٍ يُنفِّذ تلك الواجهة أن يُوفِّر تلك التوابع. على سبيل المثال، انظر إلى شيفرة الواجهة Comparable المُعرَّفة ضمن الحزمة java.lang: public interface Comparable<T> { public int compareTo(T o); } يَستخدِم تعريف تلك الواجهة معاملَ نوعٍ type parameter اسمه T، وبذلك تكون تلك الواجهة من النوع المُعمَّم generic type، وينبغي لأي صنفٍ يُنفِّذ تلك الواجهة أن: يُحدد النوع الذي يشير إليه معامل النوع T. يُوفِّر تابعًا اسمه compareTo يَستقبِل كائنًا كمعامل parameter ويعيد قيمةً من النوع int. وفي مثالٍ على ما نقول، انظر إلى الشيفرة المصدرية للصنف java.lang.Integer فيما يلي: public final class Integer extends Number implements Comparable<Integer> { public int compareTo(Integer anotherInteger) { int thisVal = this.value; int anotherVal = anotherInteger.value; return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1)); } // other methods omitted } يمتدُّ هذا الصنف من الصنف Number، وبالتالي فإنه يَرِث التوابع ومتغيرات النُّسَخ instance variables المُعرَّفة في ذلك الصنف، كما أنه يُنفِّذ أيضًا الواجهة Comparable<Integer>‎، ولذلك فإنه يُوفِّر تابعًا اسمه compareTo ويَستقبِل معاملًا من النوع Integer ويُعيد قيمةً من النوع int. عندما يُصرِّح صنفٌ معيّنٌ بأنه يُنفِّذ واجهةً معينة، فإن المُصرِّف compiler يتأكَّد من أن ذلك الصنف يُوفِّر جميع التوابع المُعرَّفة في تلك الواجهة. لاحِظ أنّ تنفيذ التابع compareTo الوارد في الأعلى يَستخدِم عاملًا ثلاثيًّا ternary operator يُكتَب أحيانًا على النحو التالي ‎?:‎. إذا لم يكن لديك فكرةٌ عن العوامل الثلاثية، فيُمكِنك قراءة مقال القيم والأنواع والعوامل في جافاسكربت. الواجهة List يحتوي إطار التجميعات في لغة جافا JCF‎ على واجهة اسمها List، ويُوفِّر تنفيذين لها هما ArrayList وLinkedList. تُعرِّف تلك الواجهة ما ينبغي أن يكون عليه الكائن لكي يُمثِل قائمةً من النوع List، ومن ثمّ فلا بُدّ أن يُوفِّر أي صنفٍ يُنفِّذ تلك الواجهة مجموعةً محددةً من التوابع، منها add وget وremove، بالإضافة إلى 20 تابعًا آخر. يوفّر كلا الصنفين ArrayList وLinkedList تلك التوابع، وبالتالي يُمكِن التبديل بينهما. ويَعنِي ذلك أنه في حالة وجود تابعٍ مُصمَّمٍ ليَعمَل مع كائنٍ من النوع List، فإن بإمكانه العمل أيضًا مع كائنٍ من النوع ArrayList أو من النوع LinkedList، أو من أيّ نوعٍ آخرَ يُنفِّذ الواجهة List. يُوضِّح المثال التالي تلك الفكرة: public class ListClientExample { private List list; public ListClientExample() { list = new LinkedList(); } private List getList() { return list; } public static void main(String[] args) { ListClientExample lce = new ListClientExample(); List list = lce.getList(); System.out.println(list); } } كما نرى، لا يقوم الصنف ListClientExample بعملٍ مفيد، غير أنه يحتوي على بعض العناصر الضرورية لتغليف قائمة من النوع List، إذ يتضمَّن متغير نسخةٍ من النوع List. سنستخدِم هذا الصنف لتوضيح فكرةٍ معينة، ثم سنحتاج إلى استخدامه في التمرين الأول. يُهيئ باني الصنف ListClientExample القائمة list باستنساخ instantiating -أي بإنشاء- كائنٍ جديدٍ من النوع LinkedList، بينما يعيد الجالب getList مرجعًا reference إلى الكائن الداخليّ المُمثِل للقائمة، في حين يحتوي التابع main على أسطرٍ قليلةٍ من الشيفرة لاختبار تلك التوابع. النقطة الأساسية التي أردنا الإشارة إليها في هذا المثال هو أنه يحاول استخدام List ، دون أن يلجأ لتحديد نوع القائمة هل هي LinkedList أم ArrayList ما لم تكن هناك ضرورة، فكما نرى متغير النسخة كيف أنه مُعرَّف ليكون من النوع List، كما أن التابع getList يعيد قيمةً من النوع List، دون التطرق لنوع القائمة في أيٍّ منهما. وبالتالي إذا غيرّت رأيك مستقبلًا وقررت أن تَستخدِم كائنًا من النوع ArrayList، فكل ما ستحتاج إليه هو تعديل الباني دون الحاجة لإجراء أي تعديلاتٍ أخرى. تُطلَق على هذا الأسلوب تسميةُ البرمجة المعتمدة على الواجهات أو البرمجة إلى واجهة. تجدر الإشارة هنا إلى أنّ الكلام هنا عن الواجهات بمفهومها العام وليس مقتصرًا على الواجهات بلغة جافا. في أسلوب البرمجة المعتمدة على الواجهات، تعتمد الشيفرة المكتوبة على الواجهات فقط مثل List، ولا تعتمد على تنفيذاتٍ معيّنةٍ لتلك الواجهات، مثل ArrayList. وبهذا، ستعمل الشيفرة حتى لو تغيّر التنفيذ المعتمد عليها في المستقبل. وفي المقابل، إذا تغيّرت الواجهة، فلا بُدّ أيضًا من تعديل الشيفرة التي تعتمد على تلك الواجهة، ولهذا السبب يتجنَّب مطورو المكتبات تعديل الواجهات إلا عند الضرورة القصوى. تمرين 1 نظرًا لأن هذا التمرين هو الأول، فقد حرصنا على تبسيطه. انسَخ الشيفرة الموجودة في القسم السابق، وأجرِ التبديل التالي: ضع الصنف ArrayList بدلًا من الصنف LinkedList. لاحظ هنا أن الشيفرة تُطبِّق مبدأ البرمجة إلى واجهة، ولذا فإنك لن تحتاج لتعديل أكثر من سطرٍ واحدٍ فقط وإضافة تعليمة import. لكن قبل كل شيء، يجب ضبط بيئة التطوير المستخدمة؛ كما يجب أيضًا أن تكون عارفًا بكيفية تصريف شيفرات جافا وتشغيلها لكي تتمكَّن من حل التمارين. وقد طُورَّت أمثلة هذا الكتاب باستخدام الإصدار السابع من عدة تطوير جافا Java SE Development Kit، فإذا كنت تَستخدِم إصدارًا أحدث، فينبغي أن يَعمَل كل شيءٍ على ما يرام؛ أما إذا كنت تَستخدِم إصدارًا أقدم، فربما لا تكون الشيفرة متوافقةً مع عدة التطوير لديك. يُفضّل استخدام بيئة تطوير تفاعلية IDE لأنها تُوفِّر مزايا إضافية مثل فحص قواعد الصياغة syntax والإكمال التلقائي لتعليمات الشيفرة وتحسين هيكلة الشيفرة المصدرية refactoring، وهذا من شأنه أن يُساعدك على تجنُّب الكثير من الأخطاء، وعلى العثور عليها بسرعة إن وُجِدت، ولكن إذا كنت متقدمًا بطلب وظيفة في شركة ما مثلًا وتنتظرك مقابلة عمل تقنية، فهذه الأدوات لن تكون تحت تصرّفك غالبًا في أثناء المقابلة، ولهذا لعلّ من الأفضل التعوّد على كتابة الشيفرة بدونها أيضًا. إذا لم تكن قد حمَّلت الشيفرة المصدرية للكتاب إلى الآن، فانظر إلى التعليمات في القسم 0.1. ستَجِد الملفات والمجلدات التالية داخل مجلد اسمه code: build.xml: هو ملف Ant يساعد على تصريف الشيفرة وتشغيلها. lib: يحتوي على المكتبات اللازمة لتشغيل الأمثلة (مكتبة JUnit فقط في هذا التمرين). src: يحتوي على الشيفرة المصدرية. إذا ذهبت إلى المجلد src/com/allendowney/thinkdast فستجد ملفات الشيفرة التالية الخاصة بهذا التمرين: ListClientExample.java: يحتوي على الشيفرة المصدرية الموجودة في القسم السابق. ListClientExampleTest.java: يحتوي على اختبارات JUnit للصنف ListClientExample. راجع الصنف ListClientExample وبعد أن تتأكَّد أنك فهمت كيف يعمل، صرِّفه وشغّله؛ وإذا كنت تستخدم أداة Ant، فاذهب إلى مجلد code ونفِّذ الأمر ant ListClientExample. ربما تتلقى تحذيرًا يشبه التالي: List is a raw type. References to generic type List<E> should be parameterized. سبب ظهور هذا التحذير هو أننا لم نُحدّد نوع عناصر القائمة، وقد فعلنا ذلك بهدف تبسيط المثال، لكن يُمكِن حل إشكاليّة هذا التحذير بتعديل كل List أو LinkedList إلى List<Integer>‎ أو LinkedList<Integer>‎ على الترتيب. يُجرِي الصنف ListClientExampleTest اختبارً واحدًا، يُنشِئ من خلاله كائنًا من النوع ListClientExample، ويَستدعِي تابعه الجالب getList، ثم يَفحَص ما إذا كانت القيمة المعادة منه هي كائن من النوع ArrayList. سيفشَل هذا الاختبار في البداية لأن التابع سيعيد قيمةً من النوع LinkedList لا من النوع ArrayList، لهذا شغِّل الاختبار ولاحظ كيف أنه سيفشل. والآن لنعدّل الشيفرة كما يلي: ضعLinkedList بدلًا من ArrayList ضمن الصنف ListClientExample، وربما تحتاج أيضًا إلى إضافة تعليمة import. صرِّف الصنف ListClientExample وشغِّله، ثم شغِّل الاختبار مرةً أخرى. يُفترضُ أن ينجح الاختبار بعد هذا التعديل. إن سبب نجاح هذا الاختبار هو تعديلك للتسمية LinkedList في باني الصنف، دون تعديلٍ لاسم الواجهة List في أي مكانٍ آخر. لكن ماذا سيحدث لو فعلت؟ دعنا نجرب. عدِّل اسم الواجهة List في مكان واحد أو أكثر إلى الصنف ArrayList، عندها ستجد أن البرنامج ما يزال بإمكانه العمل بشكل صحيح، ولكنه الآن يحدد تفاصيلَ زائدةً عن الحاجة، وبالتالي إذا أردت أن تُبدِّل الواجهة مرةً أخرى في المستقبل، فستضطّر إلى إجراء تعديلاتٍ أكثر على الشيفرة. تُرى، ماذا سيحدث لو اِستخدَمت List بدلًا من ArrayList داخل باني الصنف ListClientExample؟ ولماذا لا تستطيع إنشاء نسخة من List؟ ترجمة -بتصرّف- للفصل Chapter 1: Interfaces من كتاب Think Data Structures: Algorithms and Information Retrieval in Java. اقرأ أيضًا المقال التالي: مدخل إلى تحليل الخوارزميات الدليل السريع للغة البرمجة Java واجهة المستخدم الحديثة في جافا واجهة برمجة التطبيقات والحزم والوحدات والتوثيق Javadoc في جافا الواجهات Interfaces في جافا
  2. فيما سبق من دروس هذه السلسلة، تعلمنا أساسيات لغات البرمجة وجافا خاصة، هناك بعض الأمور الهامة في لغة جافا والتي يتم استخدامها بكثرة في تطبيقات الأندرويد، وسنكمل في هذا الدرس ما بدأناه من أساسيات لغة Java. Method Overriding لشرح هذا المفهوم دعنا نوضح هذا المثال والذي يقوم بتعريف صنف جديد يدعى Shape وبداخله ثلاث توابع كما يلي: class Shape{ protected int width; protected int height; public void setWidth(int a){ width = a; } public void setHeight(int b){ height = b; } public int getArea(){ return 0; } } وإذا قمنا بعمل كائن جديد يمكننا من خلاله استدعاء التوابع الخاصة به. Shape sh = new Shape(); sh.setWidth(10); sh.setHeight(5); int area = sh.getArea(); في المثال السابق سيتم تخزين 0 في المتغير area وذلك ما يفعله التابع ()getArea، حيث يقوم دائمًا بإعادة القيمة 0 أيًا كانت قيمة الطول والعرض وذلك لأننا نعتبر هذا الصنف نوعًا عام غير محدد الشكل ولا يمكننا معرفة مساحته. سنقوم الآن بصنع صنف جديد يرث من Shape: class Square extends Shape{ } كما ذكرنا سابقًا سيرث منه المتغيرات والخصائص كما سيرث منه التوابع الخاصة به فلا داعي لتعريفها بداخله مرة أخرى. Square sq = new Square(); sq.setWidth(10); sq.setHeight(10); int area = sq.getArea(); ستظل في هذه الحالة قيمة المتغير area كما هي تساوي 0 حيث ورث الصنف Square التابع ()getArea كما هو دون أي تغيير. إذا أردنا تغيير القيمة التي يعيدها هذا التابع نستخدم مفهوم Method Overriding، وهو ببساطة يعني تجاوز المحتوى السابق لهذا التابع والذي تم استخدامه داخل الأب لهذا الصنف واستخدام محتوى جديد بدلًا منه عند استدعاء التابع. ولتطبيق هذا المفهوم نقوم بكتابة التابع والحفاظ على اسمه ونوع البيانات التي يُعيدها وتغيير المحتوى الداخلي له لتنفيذ الوظيفة الجديدة. class Square extends Shape{ public int getArea(){ return width * height; } } والآن عند كتابة الكائن السابق سنجد أن المتغير area تغيرت قيمته ليقوم بتخزين حاصل ضرب الطول والعرض وحساب المساحة، ولن يتغير المحتوى الخاص بالتابع الأصلي ()getArea المتواجد داخل الصنف Shape. Square sq = new Square(); sq.setWidth(10); sq.setHeight(10); int area = sq.getArea(); //area = 10 والميزة الرئيسية لهذا المفهوم أنه يجعل للصنف الذي يرث من صنف آخر القدرة على صنع خطواته الخاصة لتنفيذ أحد التوابع التي يرثها دون التغيير في التابع الأصلي للأب. قواعد تطبيق مفهوم Method Overriding يجب كتابة التابع دون تغيير في الاسم الخاص به أو تغيير نوع البيانات التي تُمرر له أو تغيير ترتيبها الأصلي كما لا يمكن تغيير نوع البيانات التي يُعيدها. غير مسموح بتقليل القيود المتواجدة في التابع والتي يتم تحديدها عن طريق Access Modifiers. فمثلًا إذا تم تعريف التابع على أنه public فلا يمكن تقييده وجعله protected أو private لأن ذلك أقل في صلاحيات الوصول لهذا التابع، أما إذا كان التابع الأصلي protected وتم تغييرها إلى public فهذا مسموح به لأنه أعطى صلاحية وصول أكبر للتابع. لا يمكن تجاوز التابع المُعرف على أنه final. ولاحظ أن final عند تعريف المتغيرات تعني ثابت لا يمكن تغيير قيمته، وعند تعريف التوابع تعني تابع لا يمكن تجاوزه وتغيير محتواه. إذا أردت تنفيذ محتوى التابع الأصلي لتابع تم تجاوزه يمكنك ذلك عن طريق استخدام super، وبتطبيق ذلك على المثال السابق: class Square extends Shape{ public int getArea(){ int a = super.getArea(); if ( a == 0 ){ return width * height; } else { return a; } } } الحواشي Annotations هي طريقة لتقديم معلومات معينة عن الشيفرة المكتوبة والتي تشير إليها هذه الملاحظة ولا يتم اعتبارها أنها جزء من الشيفرة ولا تؤثر بشكل مباشرة فيها. هناك استخدامات مختلفة للحواشي annotations منها إعطاء أوامر للمترجم الخاص بالبرنامج، ولهذا الاستخدام يوجد عدة حواشي مبنية داخل جافا وهي: Override@ ويتم استخدامها عند تجاوز تابع وذلك لجعل المترجم يقوم بالتأكد أننا نقوم حقًا بتجاوز تابع متواجد داخل الأب ونكتبه بشكل صحيح غير مخالف للقواعد. class Square extends Shape{ @Override public int getArea(){ return width * height; } } Deprecated@ ويتم استخدامها لإشارة إلى أن هذا التابع أو الصنف تم إزالته من اللغة وأصبح قديمًا لذا لا يجوز استعماله مرة أخرى ويجب استبداله، وعند كتابة هذه الملاحظة يقوم المترجم بكتابة تحذير للمطور بذلك حتى يُذكّره. وتعتبر الحواشي من الأشياء المهمة عند كتابة الشيفرات ومن الجيد التعود على استخدامها. Method Overloading هي طريقة للسماح للصنف بتعريف تابع أو أكثر لها نفس الاسم. وهناك بعض الشروط الواجب توافرها لتطبيق هذا المفهوم بشكل صحيح فيجب أن تختلف التوابع ذات الاسم نفسه في إحدى هذه العناصر عدد المعاملات Parameters التي يتمر تمريرها. نوع المعاملات التي يتم تمريرها. ترتيب المُعاملات التي يتم تمريرها. class Exampe{ public int add(int x,int y){ return x+y; } public int add(int x,int y,int z){ return x+y+z; } public float add(float x,float y){ return x+y; } } في المثال السابق استخدمنا نفس التابع ()add ولكن بأشكال مختلفة وقمنا بتطبيق إحدى القواعد المطلوبة في كل تابع لتحقيق شرط هذا المفهوم. ولاحظ أنه لا يمكننا كتابة هذا الشكل: public int add(int y,int x){ return x+y; } حيث أنه لا يوجد فرق بينه وبين الشكل الأول فلا يمكننا التمييز بتغيير اسم المتغيرات. ArrayList يوجد داخل لغة جافا صنف يدعى ArrayList وهو يعبر عن قائمة من البيانات تتميز بالمرونة والقدرة على القيام بوظائف عديدة، ويتم تفضيلها عادة على استخدام مصفوفة البيانات التقليدية وذلك لأن المصفوفة يتم تحديدها بعدد من البيانات لا يمكن تغييره، فلا يمكن إضافة عناصر جديدة لها كما لا يمكن إزالة عناصر منها وتقليل العدد. وهذا ما يميز ArrayList لقدرتها على تغيير حجمها والتكييف حسب البيانات المخزنة بداخلها. كما تتميز ArrayList عن Array أو المصفوفة التقليدية بوجود توابع مختلفة تقوم بوظائف عديدة على عكس Array الذي يملك توابع. لتعريف كائن جديد من الصنف ArrayList: ArrayList<String> strObject = new ArrayList<String>(); في المثال السابق قمنا بتعريف كائن من الصنف ArrayList يدعى strObject ويستطيع تخزين بداخله بيانات من النوع String. الآن بعد أن قمنا بتعريف الكائن هناك عدة توابع يمكننا استخدامها مثل: (add(o وهو يقوم بإضافة العنصر (o) إلى القائمة. strObject.add(“Ahmed”); // [“Ahmed”] strObject(“Mohamed”); // [“Ahmed”,”Mohamed”] strObject(“Mariam”); // [“Ahmed”,”Mohamed”,”Mariam”] في المثال السابق نضيف عناصر من النوع String إلى القائمة باستدعاء التابع ()add وتمرير له العنصر الذي نريد إضافته، وفي كل مرة يتم استدعاء التابع يتم تغيير حجم القائمة بشكل مرن. (add(I ,o يختلف عن التابع السابق بأنه يقوم بتحديد المكان (I) الذي يرغب بتخزين العنصر فيه، ففي التابع السابق يتم إضافة العناصر في ذيل القائمة. strObject.add(2,“Sara”); // [“Ahmed”,”Mohamed”,”Sara”,”Mariam”] (set(I,O تقوم بتبديل العنصر المتواجد في المكان (I) بالعنصر (O). strObject.set(1,“Tarek”); // [“Ahmed”,”Tarek”,”Sara”,”Mariam”] في المثال السابق سيتم استبدال العنصر "Mohamed" بالعنصر "Tarek". لاحظ أنه يبدأ الترقيم الخاص بالعناصر من صفر. (get(I يُعيد هذا التابع العنصر المخزن في المكان (I). String name = strObject.get(0); //nama = “Ahmed” في المثال السابق نحصل على العنصر "Ahmed" والمتواجد في المكان 0 (رأس القائمة). ()size لمعرفة عدد العناصر المخزنة داخل القائمة. int numberOfElements = strObject.size(); // numberOfElements = 4 (remove(O لإزالة عنصر محدد من القائمة. strObject.remove(“Mariam”); // [“Ahmed”,”Tarek”,”Sara”] (remove(I لإزالة عنصر المتواجد في المكان (I). strObject.remove(1); // [“Ahmed”,”Sara”] ()clear لإزالة كافة عناصر القائمة. strObject.clear(); // [] وكما قمنا باستخدام ArrayList مع النصوص يمكننا استخدامها مع أي صنف أخر فمثلا يمكنا عمل قائمة من المربعات (صنف Square)، وهو الصنف الذي قمنا بصناعته في أول الدرس. ArrayList<Square> obj = new ArrayList<Square>(); ولإضافة مربع جديد للقائمة. Obj.add(new Square()); ولتغيير الطول والعرض الخاصين بهذا المربع. Obj.get(0).setWidth(15); Obj.get(0).setHeight(15); وهكذا يمكننا التعامل مع عناصر القائمة بنفس الطريقة، فكل عنصر داخل القائمة هو كائن من Square. لاحظ أنه هناك توابع خاصة بالصنف ArrayList وأخرى خاصة بالصنف Square. Interface تتشابه الواجهات (Interface) في بينتها مع الأصناف (Class)، فيمكننا بداخله تعريف توابع ومتغيرات لكنها ذات طبيعة خاصة. فجميع التوابع داخل الواجهة تتكون من تعريف فقط ولا يوجد لها محتوى، وعلى الصنف الذي يُنفذ الواجهة أن يكتب المحتوى الخاص بالتابع. public interface MyInterface { public int method1(); public void method2(); } ولا يمكن إنشاء كائنات من الواجهة (Interface). ولتنفيذ الواجهة نستخدم implements، ويمكن للأصناف (Classes) فقط أن تُنفذ الواجهات (Interfaces). وبداخل الصنف يجب كتابة المحتوى الخاص بالتوابع التي تم تعريفها داخل الواجهة، كما يمكننا أن نكتب التوابع الخاصة بالصنف كما سبق. public class X implements MyInterface{ public int method1(){ return 0; } public void method2(){ } } بعد ذلك يمكننا إنشاء كائنات من الصنف X واستدعاء التوابع كما فعلنا سابقًا. X obj = new X(); Obj.method2(); واستخدام الواجهة هو الطريقة المثالية لتزويد المطور بالتوابع اللازم كتابتها لأداء مهمة ما، فهي تضمن أن الصنف الذي يُنفذ الواجهة قد قام بكتابة كافة التوابع الخاصة به.
  3. يتحدّث هذا الدرس عن موضوعين مهمّين في سي شارب ألا وهما الواجهات Interfaces والمجموعات Collections. تُعتبر المجموعات collections من المزايا القويّة والمهمّة في سي شارب، وهي وسيلة لتخزين العناصر ضمن بنية قابلة للتوسّع تلقائيًّا وسهلة التعامل. إذًا فهي تشبه المصفوفات، باستثناء أنّ المصفوفات ذات حجم ثابت وهي تبقى كذلك منذ لحظة إنشائها، أمّا المجموعات فهي على النقيض من ذلك، فهي بنية قابلة للتوسّع بقدر ما نرغب. بالإضافة إلى أنّ للمجموعات أشكال عديدة مفيدة جدًّا، فهناك المكدّس stack والرتل queue والقاموس dictionary وغيرها من البنى المهمّة. سنتناول في هذا الدرس المجموعات العاديّة، والمجموعات العموميّة generics collections. سنبدأ هذا الدرس بالحديث المختصر عن الواجهات، وهي تشبه الأصناف ولكن بصورة مجرّدة، حيث تصف سمات عامّة ينبغي أن تتحلّى بها الأصناف التي تحقّقها. كلمة عن الواجهات ليست فكرة الواجهات Interfaces محصورةً بسي شارب، فهي موجودة أيضًا في لغات برمجة أخرى مثل جافا Java. تشبه الواجهة Interface الصنف Class بشكل كبير، فهي تضم خصائص وتوابع، ولكن كتصاريح فقط، بدون شيفرة تحوي عبارات برمجيّة، وبدون محدّدات وصول. كما لا تحتوي على حقول fields ولا على بواني. تستطيع القول أنّ الواجهة تعرّف الهيكل العام بشكل مجرّد فحسب. يمكن الاستفادة من الواجهة عندما نحقّقها implement. فعندما يرث صنف ما من واجهة، فيجب عليه أن يعيد التصريح عن جميع الخصائص والتوابع الموجودة ضمن هذه الواجهة ولكن مع تزويدها بحواضن تحوي الشيفرة المطلوب تنفيذها. فكأنّ الواجهة تحقّق مبدأ التعدديّة الشكليّة بشكل ممتاز، فهي تصرّح عن تابع أو خاصيّة، وتترك للصنف الذي يحقّقها (يرث منها) حريّة التعبير عن هذا التابع أو هذه الخاصيّة بالشكل الملائم. تُستخدم الواجهات على نحو واسع جدًّا في سي شارب، وتتبع تسميتها لنفس أسلوب تسمية الأصناف، مع الاصطلاح على أن يكون الحرف الأوّل من اسم أيّ واجهة هو الحرف الطباعي الكبير I وهو الحرف الأوّل من كلمة Interface. في الحقيقة لقد حلّت الواجهات مشكلة الوراثة المتعدّدة (الوراثة من أكثر من صنف بنفس الوقت) والتي كانت تتمتّع بها لغة C++ فقط. الآن أصبح بإمكان أي صنف أن يرث من صنف آخر واحد فقط، بالإضافة إلى تحقيقه لأي عدد يرغبه من الواجهات. يُصرّح عن الواجهة بالكلمة المحجوزة interface. انظر البرنامج Lesson10_1 الذي يوضّح استخدام الواجهات. هل تذكر الأصناف Animal و Bird و Frog و Fish؟ لقد استعرت الصنفين Animal و Frog لتوضيح فكرة استخدام الواجهات، تذكّر أنّ الصنف Frog كان يرث من الصنف Animal. في الحقيقة لقد استفدت من فكرة أنّ الكائنات الحيّة تتنفّس Breathing. لذلك أنشئت واجهة اسمها IBreathing لتعبّر عن عمليّة التنفّس، وبما أنّ الضفدع Frog هو كائن حيّ، فمن الطبيعي أن يحقّق هذه الواجهة. تحتوي الواجهة IBreathing على تابع وحيد اسمه TakeBreath (السطر 9) يقبل وسيطًا واحدًا من النوع double يُعبّر عن كميّة الأكسجين التي سيحصل عليها الكائن الحيّ عند التنفّس، كما يُرجع هذا التابع قيمة من النوع double أيضًا تمثّل كميّة الأكسجين التي بقيت بعد عمليّة التنفّس. يرث الصنف Frog هذه الواجهة في السطر 20، ويحقّقها من خلال إعادة تعريف التابع TakeBreath في الأسطر من 27 حتى 30 حيث يُعبّر عن عمليّة التنفّس بالشكل الذي يناسبه (سيستهلك في مثالنا هذا 20% من كميّة الأكسجين التي يحصل عليها). في الحقيقة يمكن استخدام الواجهة IBreathing مع أيّ "كائن حيّ" بصرف النظر عن كونه يرث من الصنف Animal (الذي يمثّل الصنف الحيواني) أو من الصنف Mammal (الثديّيات) مثلًا أو غيره، وذلك لأنّ جميع الكائنات الحيّة تشترك معًا بخاصيّة التنفّس، وهنا تمكن قوّة الواجهات. 1 using System; 2 3 namespace Lesson10_01 4 { 5 class Program 6 { 7 interface IBreathing 8 { 9 double TakeBreath(double oxygen_amount); 10 } 11 12 class Animal 13 { 14 public virtual void Move() 15 { 16 Console.WriteLine("Animal: Move General Method"); 17 } 18 } 19 20 class Frog : Animal, IBreathing 21 { 22 public override void Move() 23 { 24 Console.WriteLine("Frog - Move: jumping 20 cm"); 25 } 26 27 public double TakeBreath(double oxygen_amount) 28 { 29 return oxygen_amount * 0.8; 30 } 31 32 } 33 34 static void Main(string[] args) 35 { 36 double oxygent_to_breath = 10; 37 38 IBreathing frog = new Frog(); 39 40 Console.WriteLine("Oxygen amount before breath: {0}", oxygent_to_breath); 41 Console.WriteLine("Oxygen amount after breath: {0}", frog.TakeBreath(oxygent_to_breath)); 42 } 43 } 44 } هناك أمر آخر جدير بالملاحظة. انظر إلى السطر 38 ستجد أنّنا قد صرّحنا عن المتغيّر frog من النوع IBreathing ثمّ أسندنا إليه مرجعًا لكائن من النوع Frog، وهذا أمر صحيح تمامًا وشائع جدًّا لأنّ الصنف Frog يرث من الواجهة IBreathing. عند تنفيذ البرنامج ستحصل على الخرج التالي: Oxygen amount before breath: 10 Oxygen amount after breath: 8 المجموعات في سي شارب المجموعات العادية توجد الأصناف المعبّرة عن هذه المجموعات ضمن نطاق الاسم System.Collection. تلعب نطاقات الأسماء دورًا تنظيميًّا للأصناف، وسنتحدّث عنها بشكل أكبر في درس لاحق. من أبرز المجموعات في نطاق الاسم هذا هو الصنف ArrayList. يحقّق الصنف ArrayList كلّ من الواجهات IList و ICollection و IEnumerable و ICloneable. جميع هذه الواجهات تقع في مكتبة FCL في إطار عمل دوت نت، حيث تعرّف هذه الواجهات العمليّات الأساسيّة التي ينبغي أن يتمتّع بها الصنف ArrayList. تسمح الكائنات من هذه المجموعة بإضافة أي نوع من العناصر لها، حيث من الممكن أن نضيف عناصر من النوع object. يمكن إضافة العناصر إلى هذه المجموعة باستخدام التابع Add الذي يقبل وسيطًا من النوع object. أي أنّنا فعليًّا نستطيع أن نضيف عناصر من أنواع مختلفة لنفس المجموعة. أيّ عنصر تتمّ إضافته سيوضع آخر المجموعة التي هي ذات حجم مرن، فمن الممكن إضافة أي عدد نرغبه من العناصر. أمّا إذا أردنا إضافة عنصر إلى مكان محدّد ضمن القائمة، فعلينا استخدام التابع Insert الذي يحتاج إلى وسيطين، الأوّل هو الدليل المراد إدراج العنصر الجديد ضمنه، والثاني هو العنصر نفسه. انظر البرنامج Lesson10_02 البسيط التالي: 1 using System; 2 using System.Collections; 3 4 namespace Lesson10_02 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 ArrayList values = new ArrayList(); 11 12 values.Add("My "); 13 values.Add("age: "); 14 values.Add(36); 15 16 foreach(object item in values) 17 { 18 Console.Write(item); 19 } 20 21 Console.WriteLine(); 22 } 23 } 24 } استطعنا الحصول على عناصر هذه المجموعة باستخدام حلقة foreach. ولكن إذا أردنا الوصول إلى عنصر محدّد فحسب، ولنقل أنّه العنصر الثالث (القيمة 36) في مثالنا السابق، فيمكن ذلك من خلال الشكل التالي: values[2] تذكّر دومًا أنّ دليل العنصر الأوّل هو 0. في الواقع ستكون القيمة التي سنحصل عليها من [values[2 هي قيمة من نوع object رغم أنّها في حقيقة الأمر تحوي القيمة 36 وهي قيمة من نوع int بطبيعة الحال. السبب في ذلك منطقيّ وهو أنّنا عندما أضفنا القيمة 36 إلى المجموعة كان ذلك باستخدام التابع Add الذي يقبل وسيطًا من النوع object. إذا أردنا الاستفادة من القيمة الفعليّة المخزّنة ضمن [values[2 فعلينا هنا أن نستخدم عامل التحويل (int) على الشكل التالي: int age = (int) values[2]; ملاحظة: عند تمرير القيمة 36 في المثال السابق إلى التابع Add الذي يتوقّع وسيط من نوع object تحدث ظاهرة نسميها التعليب boxing. حيث تُعلَّب القيمة 36 ليصبح بالإمكان تمريرها مكان وسيط يتطلّب النوع object (يبقى هذا الأمر صحيحًا من أجل أي قيمة value type). أمّا عندما نريد استرجاع القيمة الفعليّة فإنّنا نقوم بعمليّة معاكسة تدعى بإلغاء التعليب unboxing باستخدام عامل التحويل بين الأنواع كما فعلنا بالعبارة البرمجيّة الأخيرة: int age = (int) values[2]; هناك العديد من المجموعات الأخرى الموجودة ضمن نطاق الاسم System.Collection، ولكن لن أتحدّث عنها هنا. في الحقيقة إذا أردت نصيحتي حاول ألّا تستخدم المجموعات العاديّة أبدًا! يكمن السبب في ذلك في الفقرة التالية عندما نتحدّث عن المجموعات العموميّة generic collection، حيث سنطّلع على مجموعات تشبه إلى حدٍّ بعيد المجموعات العاديّة الموجودة هنا، ولكنّها عمليّة وأكثر أمانًا. المجموعات العمومية تشبه المجموعات العموميّة generic collections من حيث المبدأ المجموعات العاديّة باستثناء أنّها أكثر أمنًا وأفضل أداءً. حيث ينبغي تعيين نوع العناصر التي ستتعامل معها المجموعة عند التصريح عنها، فتتعامل المجموعة في هذه الحالة مع نوع مُحدّد. من أشهر المجموعات العموميّة هي المجموعة <List<T وهي تعبّر عن القائمة list. قد يبدو الشكل السابق غريبًا قليلًا، ولكنّه في الحقيقة بسيط. استبدل الحرف T بأيّ نوع (صنف) ترغبه وستقبل المجموعة نتيجة لذلك أن يكون عناصرها من هذا النوع. تقع المجموعات العموميّة في نطاق الاسم System.Collections.Generic. سنعدّل البرنامج Lesson09_02 من الدرس السابق الذي كان يسمح بإدخال أسماء ودرجات خمسة طلاب فقط، ويخزّنها على شكل كائنات Student ضمن مصفوفة من النوع []Student وذلك لإيجاد مجموع الدرجات والمعدّل. سنجعل هذا البرنامج يستخدم المجموعة العموميّة <List<Student (مجموعة يمكن لعناصرها تخزين مراجع لكائنات من النوع Student)، سننشئ البرنامج Lesson10_03 لهذا الغرض. 1 using System; 2 using System.Collections.Generic; 3 4 namespace Lesson10_03 5 { 6 class Student 7 { 8 public string Name { get; set; } 9 public int Mark { get; set; } 10 } 11 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 List<Student> listStudents = new List<Student>(); 17 int sum = 0; 18 bool continueCondition = true; 19 int counter = 0; 20 string response; 21 22 Console.WriteLine("Input Students Marks"); 23 Console.WriteLine("====================="); 24 25 //input loop. 26 while(continueCondition) 27 { 28 Student student = new Student(); 29 30 Console.Write("Input student {0} th name: ", counter + 1); 31 student.Name = Console.ReadLine(); 32 33 Console.Write("Input student {0} th mark: ", counter + 1); 34 string tmpMark = Console.ReadLine(); 35 student.Mark = int.Parse(tmpMark); 36 37 listStudents.Add(student); 38 39 Console.WriteLine(); 40 Console.Write("Add another student? (y/n) : "); 41 response = Console.ReadLine(); 42 43 if(response=="n" || response == "N") 44 { 45 continueCondition = false; 46 } 47 48 counter++; 49 } 50 51 Console.WriteLine(); 52 Console.WriteLine("Students Marks Table"); 53 Console.WriteLine("===================="); 54 Console.WriteLine("No\tName\tMark"); 55 56 //calculating sum and display output loop. 57 for (int i = 0; i < listStudents.Count; i++) 58 { 59 sum += listStudents[i].Mark; 60 Console.WriteLine("{0}\t{1}\t{2}", i + 1, listStudents[i].Name, listStudents[i].Mark); 61 } 62 63 Console.WriteLine("-------------------"); 64 Console.WriteLine("Sum\t\t{0}", sum); 65 Console.WriteLine("Average\t\t{0}", sum / (double)listStudents.Count); 66 } 67 } 68 } لقد أجرينا هنا بعض التحسينات. بدأنا البرنامج في السطر 16 بالتصريح عن المتغيّر listStudents من النوع <List<Student وإنشاء كائن من هذا النوع وإسناده لهذا المتغيّر. تقبل المجموعة العموميّة <List<Student بتخزين كائنات من النوع Student ضمنها. لاحظ أنّنا لم نحدّد عدد الكائنات مسبقًا (مع أنّه يمكن ذلك بهدف تحسين الأداء لا غير). وضعنا حلقة while في السطر 26 بدلًا من حلقة for القديمة وذلك لأنّنا لا نعرف على وجه التحديد عدد الطلّاب الذين يرغب المستخدم بإدخال بياناتهم. لاحظ شرط استمرار الحلقة continueCondition الذي يحمل القيمة true بشكل افتراضيّ. أصبح البرنامج غير مقيّدٍ بعدد محدّد من الطلاب، فبعد إدخال بيانات كل طالب، سيعرض البرنامج رسالة يخيّر فيها المستخدم في إضافة المزيد أم التوقّف (السطر 40) فإذا اختار المستخدم التوقّف بإدخاله النص "N" أو "n" عندها سيسند البرنامج القيمة false للمتغيّر continueCondition مما يؤدّي إلى الخروج من حلقة while عند بدء الدورة التالية. تنحصر وظيفة المتغيّر counter الذي صرّحنا عنه في السطر 19 في إظهار ترتيب الطالب الحالي على الشاشة. نستخدم الخاصيّة Count للمجموعة listStudents لمعرفة عدد العناصر الفعليّة المخزّنة ضمنها (تذكّر الخاصيّة Length المماثلة لها في المصفوفات). بعد تنفيذ البرنامج وإدخال بيانات ستة طلّاب، ستحصل على شكل شبيه بما يلي: يوجد تابع اسمه RemoveAt ضمن هذه المجموعة يسمح بإزالة عنصر من القائمة، حيث نمرّر لهذا التابع دليل index العنصر المراد إزالته (دليل العنصر الأوّل هو 0) ليعمل هذا التابع على إزالته وإعادة تعيين أدلّة جميع العناصر بعد إزالة العنصر المطلوب. انظر الشيفرة التالية: List<string> listStrings = new List<string>(); listStrings.Add("Bird"); listStrings.Add("Fish"); listStrings.Add("Frog"); listStrings.RemoveAt(1); أنشأنا في الشيفرة السابقة مجموعة من النوع <List<string (عناصرها من النوع string)، ثمّ أضفنا إليها ثلاثة عناصر. يؤدّي استدعاء التابع (RemoveAt(1 إلى إزالة العنصر ذو الدليل 1 من هذه المجموعة، أي أنّ العنصر ذو القيمة Fish سيُزال من هذه القائمة. يوجد تابع مشابه لهذا التابع اسمه Remove يتطلّب أن تمرّر إليه مرجعًا لكائن موجود في هذه المجموعة لتتم إزالته. فإذا كان النوع العمومي لهذه المجموعة عبارة عن نوع قيمة مثل <List<int أو <List<double فعندها يكفي تمرير القيمة المراد إزالتها للتابع Remove فحسب. علمًا أنّ هذا التابع يزيل أوّل نتيجة تطابق يصادفها في هذه المجموعة. يوجد أيضًا التابع Reverse الذي يعمل على عكس ترتيب العناصر الموجودة في المجموعة، حيث يصبح العنصر الأوّل هو الأخير، والعنصر الأخير هو الأوّل. كما يوجد التابع Sort الذي يعمل على ترتيب العناصر ضمن المجموعة وفق الترتيب الافتراضي (بالنسبة للأنواع المضمّنة) أو وفق ترتيب كيفيّ يمكن للمبرمج أن يختاره. وهناك تابع مفيد آخر وهو BinarySearch الذي يجري خوارزمية البحث الشهيرة على عناصر المجموعة، حيث نمرّر إليه القيمة المراد البحث عنها (أو مرجع الكائن الذي نريد البحث عنه) ويُرجع هذا التابع دليل العنصر ضمن المجموعة في حال وجده. مع الانتباه إلى أنّ هذا الدليل يمثّل دليل العنصر ضمن المجموعة على اعتبارها مرتّبة. إذ أنّه يقوم بترتيبها بشكل داخليّ قبل أن يجري عملية البحث. إذا أردت الحصول على نتائج منطقيّة، فاعمل على ترتيب مجموعتك باستخدام التابع Sort قبل استدعاء BinarySearch. يمكننا الوصول إلى عنصر محدّد ضمن مجموعة عموميّة بنفس الأسلوب التي تحدّثنا عنه في المجموعات العاديّة، مع ملاحظة أنّنا لن نحتاج إلى عامل التحويل بين الأنواع، وبالتالي التخلّص من عمليتيّ التعليب boxing وإلغاء التعليب unboxing. كما يمكن الاستفادة أيضًا من التابع Insert للإدراج ضمن موقع مُحدّد، والذي تحدّثنا عنه أيضًا في المجموعات العاديّة. توجد توابع أخرى مفيدة ضمن المجموعة <List<T ولكنّنا لن نستطيع الخوض فيها قبل أن نتحدّث عن النوّاب delegates في درس لاحق. ملاحظة: توجد طريقة سريعة وفعّالة لإنشاء مجموعة <List<T وإسناد عناصر إليها مباشرةً في حال كان عدد العناصر محدّد ومعروف سلفًا. فإذا أردنا إنشاء مجموعة من النوع <List<int تحوي العناصر 1، 2، 5، 10 يمكنك كتابة العبارة التالية لهذا الغرض: List<int> listNumbers = new List<int>() { 10, 5, 2, 1 }; تنشئ هذه العبارة مجموعة من النوع <List<int وتضيف إليها العناصر 10 و5 و2 و1 ثمّ تسند هذه المجموعة إلى المتغيّر listNumbers. تمارين داعمة تمرين 1 اكتب برنامجًا يطلب من المستخدم إدخال خمس قيم نصيّة، ويخزّنها ضمن مجموعة من النوع <List<string. ثمّ استخدم التابع Reverse لعكس ترتيب العناصر ضمن هذه المجموعة، ثمّ اطبع النتائج على الشاشة. تمرين 2 اكتب برنامجًا يطلب من المستخدم إدخال قيم عدديّة من النوع double بقدر ما يريد، وبعد أن يفرغ من الإدخال، احسب المتوسّط الحسابي (المعدّل) لهذه الأعداد، ورتّبها باستخدام التابع Sort، ثم اطبعها على الشاشة، مع المتوسّط الحسابي لها. (تلميح: استفد من البرنامج Lesson10_03 لسؤال المستخدم هل يريد إضافة عدد جديد أم لا) الخلاصة تعرّفنا في هذا الدرس على الواجهات Interfaces والمجموعات Collections. من النادر أن يخلو أيّ برنامج فعليّ من استخدام المجموعات أو الواجهات، وفي الحقيقة هناك العديد من بنى المجموعات المفيدة التي لم نتناولها في هذا الدرس. سنحاول أن نتوسّع في المزايا القويّة والرّائعة للمجموعات في سلسلة قادمة.
  4. التجريد – Abstraction نعلم أنَّ الصنف المُشتَق يأخذ خاصياته من الصنف الأب لكن الصنف المُشتَق مستقل تمامًا عن الصنف الأب؛ وقد يكون في بعض الأحيان من الجيد أن نرسم خطوطًا عريضة لآلية سلوك الصنف الابن، وهذه هي مهمة الأصناف والدوال المجردة. إذ أنَّ الصنف المجرد يحتوي على دوال غير مكتملة (أي مجردة) التي يجب أن يملأها الابن لكي يكون صنفًا وعدا ذلك سيكون صنفًا مجردًا أيضًا. بكلامٍ آخر، الصنف المُجرَّد (Abstract Class) هو صنف يحتوي على أسماء دوال دون كتابة الشيفرات المسؤولة عن عملها وتسمى تلك الدوال بالدوال المجردة، وقد يحتوي أيضًا على دوال كاملة اعتيادية تؤدي وظيفتها تمامًا. انظر إلى المثال الآتي لمزيدٍ من الإيضاح: <?php // تعريف صنف مجرد abstract class AbsClass { function __construct() { echo "this is an abstract class <br>"; } // دالة مجردة abstract public function abs_function(); function full_function() { echo "this is not an abstract function <br>"; } } class SubClass extends AbsClass { function __construct() { echo "this is child class <br>"; parent::full_function(); } // تعريف الدالة المجردة public function abs_function() { echo "this is completed abstract function <br>"; } } $obj = new SubClass(); $obj->abs_function(); ?> نستعمل الأصناف المجردة عندما يلزمنا إنشاء طريقة محددة للتعامل مع عدِّة أصناف مُشتقَّة، التي قد تتشارك ببعض الوظائف. ملاحظة: لا يمكن إنشاء كائن من صنف مجرد، حيث لا يمكن إلا اشتقاق تلك الأصناف. يستعمل الصنف المجرد لتقييد عمل الصنف الابن. الواجهات interfaces من بين الحالات التي نستعمل فيها الواجهات (interfaces) هي عندما نريد تطبيق التعددية الشكلية أي أن تكون طريقة تعاملنا متشابهة مع عدِّة أصناف. الواجهة هي مجموعة من الدوال المجردة أي أنك تعرف اسم الدالة مع المعاملات التي تقبلها لكن دون تحديد طريقة عمل الدالة، ويمكن للصنف أن يستعمل أكثر من واجهة، لكن يجب أن يعيد تعريف كل الدوال الموجودة في تلك الواجهة، انظر إلى المثال الآتي لأخذ فكرة عن الواجهات: <?php // تعريف واجهة interface MyInterface { // abstract functions // all must be public public function display(); public function another($argument); } // واجهة أخرى interface AnotherInterface { public function complete_it(); public function one_more(); } class Parent { function parent_fun() { echo "parent function"; } } // صنف يشتق صنفًا آخر ويستعمل واجهة class Demo extends Parent implements MyInterface, AnotherInterface { public function display() { echo "display complete"; } public function another($argument) { #code } public function complete_it() { #code } public function one_more() { #code } } ?> نستعمل الواجهات عندما نريد إنشاء طريقة موحدة للتعامل مع عدِّة أصناف، فمثلًا، نُنشِئ واجهة اسمها Database فيها دوال مجردة مثل select و insert وغيرها، ثم نستعمل تلك الواجهة في صنف MySQL وفي صنف SQLite، ونعيد تعريف الدوال الموجودة في الواجهة بما يلائم طريقة عمل كل نوع من أنواع قواعد البيانات. وبهذه الطريقة نستطيع أن نستعمل قواعد بيانات MySQL أو SQLite بنفس الآلية تمامًا. ملاحظة: يجب أن تكون جميع الدوال داخل الواجهة عامةً، يمكن أن يرث صنفٌ ما صنفًا آخر ويستعمل واجهة بنفس الوقت، لكن يجب أن يكون تعريف الاشتقاق قبل الواجهات. السمات Traits قدم الإصدار 5.4.0 من PHP ميزة السّمات Traits، وهي طريقة تسمح بإعادة استعمال الشيفرات في اللغات التي لا تسمح بالوراثة المتعددة، وهي تقلل من المحدوديات الناتجة عن عدم السماح بالوراثة المتعددة عن طريق إتاحة استعمال مجموعة من الدوال في عدة أصناف. أي لو كانت عندك مجموعة من الدوال العامة، وترغب في مشاركتها بين أكثر من صنف، فضعها في Trait ثم استعملها (use) في تلك الأصناف. يُعرف Trait عبر ذكر الكلمة المحجوزة trait متبوعةً باسمه، ثم تُعرَّف الدوال داخله. وتُستعمل الكلمة use عند تعريف صنف يستعمل Trait معين كما في المثال الآتي: <?php trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class World { use HelloWorld; } $obj = new World(); $obj->sayHello(); // ستُطبع عبارة Hello World! ?> يمكن إعادة تعريف الدوال داخل الأصناف التي تستعمل Trait معيّن، كما في المثال الآتي: <?php trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class World { use HelloWorld; } class NewWorld { use HelloWorld; public function sayHello() { echo 'Hello Universe!'; } } $obj1 = new World(); $obj1->sayHello(); // ستُطبع عبارة Hello World! $obj2 = new NewWorld(); $obj2->sayHello(); // ستُطبع عبارة Hello Universe! ?> يمكن استعمال أكثر من Trait في نفس الصنف عبر ذكر أسمائهم في عبارة use مفصولًا بينهم بفاصلة، لاحظ أنه إذا عُرِّفَت دالتين بنفس الاسم في أكثر من Trait، ثم استعملناها في صنف، فسيحدث تضارب وتظهر رسالة خطأ fetal error، ويمكنك حل مشكلة التضارب في الأسماء عبر استعمال المعامل insteadof أو عبر as كما في المثال الآتي: <?php trait A { public function smallTalk() { echo 'a'; } public function bigTalk() { echo 'A'; } } trait B { public function smallTalk() { echo 'b'; } public function bigTalk() { echo 'B'; } } class Talker { // لدينا في A و B دالتين اسمهما bigTalk و smallTalk // ما يلي سيجعل الصنف يستعمل الدالة smallTalk من B عوضًا عن مثيلتها في A // و bigTalk من A عوضًا عن B use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; } } class Aliased_Talker { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; // لاحظ كيف استعملنا as لتغير اسم الدالة في الصنف B::bigTalk as talk; } } ?> مصادر مقالة Abstraction and Interface in PHP لصاحبها Harish Kumar فصل Objects في كتاب Practical PHP Programming فصل البرمجة غرضية التوجه في كتاب تعلم البرمجة بلغة PHP صفحات Object Interfaces و Traits في دليل PHP وغيرها.
×
×
  • أضف...