OOP | البرمجة بمنهجية الأصناف والأشياء OOP الجزء (05)

من المفاهيم المرتبطة بالـ OOP هو مفهوم التجريد Abstraction والذي سبق وشرحناه ولكن لم ننوه إلى الاسم وهو وصف الصنف لأن تعريف الصنف هو توصيف للقالب الذي يجري تشكيل الكائنات على أساسه وهذا التوصيف يكون مجردا لأنه لا يتضمن قيم وإنما فقط توصيف

فقط الكائن Object المُنشأ منه هو الذي سيحوي قيم في لحظة ما وهنا لم يعد مجردا وإنما محددا بدقة

من هنا عندما ذكر الفن التجريدي لأنه لا يرسم أشياء واقعية وإنما رمزية تعبر عن أشياء واقعية

بمعنى لو قام الفنان بعمل بعض "الشخابيط" التي توحي في ظاهرها أنها تشبه الطائر فهو يريد أن يصل إلى ذهن المشاهد أن هذا يعبر عن طائر ما بدون تعيين

ولا حتى نوع ذاك الطائر إنما طائر فحسب.

فمثلا عندما قلنا أننا سنعرف صنف Class يعبر عن شخص تم ذكر أن من مكونات هذا الصنف خاصية تعبر عن الاسم الأول واسم الشهرة والنوع والعمر

ولكن لم نحدد ما هو الاسم و ما هو النوع وكم سيكون العمر

لأن هذه الخصائص ستأخذ قيمها عند إنشاء كائن نوعه من هذا الصنف حينها سيكون له نفس الخصائص ولكن بقيم معينة والتي من الممكن أن تتغير أو تبقى ثابتة أثناء سير البرنامج أو لحين إفناء هذا الكائن من خلال إعلان وفاته

إذا فالتجريد هو التوصيف الذي لا يحدد قيما، وإنما يقوم بتحديد الشكل والنمط الذي سيكون عليه الكائنات المتولدة من الصنف

أما مفهوم التغليف من نوع الثاني ويدعى الكبسلة Capsulation ومنها جاءت كلمة كبسولة الدواء أو كبسولة الفضاء أي غلاف يحوي أشياء لا نستطيع الاطلاع عليها مباشرة

تخيل معي أنك اشتريت طابعة بدون صندوق أي بدون غلافها البلاستيكي

قد يقوم شخص خطأ بإدخال الورق في غير مكانها المخصص مما يؤدي إلى حدوث مشكلة أو عدم سحب الورق وتحقق الطباعة

قد يقع بالخطأ قطعة معدنية داخل أجزاء الطابعة مما تؤدي إلى عطبها في حال كانت تعمل

قد يقوم أحد الأطفال بتقطيع الأسلاك الداخلية

من هنا نرى أهمية التغليف أو الكبسلة وهي تغليف كل ما داخل الصنف وإبراز ما يهم المستخدم

إن هذا الأمر أدى إلى إنشاء آلية تضبط عملية الولوج والإخراج من الصنف بما يحمي الجسم الداخلي من الخلل و بما يضمن إعطاء نتائج صحيحة أو عدم إعطاء نتائج خاطئة و من ثم وسمها بأنها صحيحة

إن البرمجة كائنية المنحى جاءت لجعل المعطيات والوظائف في قالب واحد وضمن نوع يغلفها كوحدة واحدة

فالمعطيات هي التي يتم حفظها من خلال المتغيرات والوظائف هي التي نعرفها بعدة أسماء منها الدوال ومنها الإجراءات ومنها المناهج وهي الكتلة التي يتم تنفيذها حين استدعائها بالاسم ومن الممكن أن نمرر لها قيم وترجع لنا بنتائج عند الحاجة

المتغيرات معروفة ما هي، فهي أسماء من نوع معطيات تحمل قيم مختلفة أثناء سير البرنامج

والوظائف كما ذكرناها قبل قليل

لكن طبيعة البرمجة الكائنية أدت إلى إيجاد شكل خاص من المعطيات و هي الخصائص Properties

والخاصية Property هي متغير ولكن بسلوك معين

صحيح يسمح بتعريف متغيرات مباشرة (أي الاعتيادية) ضمن الصنف ولكن إن جعلناها عامة فنكون قد أفقدنا خاصية الحماية من أخطاء الاستخدام لأننا جعلنا تغيير القيم يتم بشكل مباشر من قبل الجميع

كمن يضع مفتاح فصل كهرباء المدينة مكشوفا أمام الناس، بدلا من وضعه في غرفة محكمة الإغلاق لا يفتحها إلا من هو مخول بذلك. أو كمن يجعل المكتبة بدون أمين وترك كل الناس تأخذ الكتب مباشرة من الرفوف أو وضع الكتب مباشرة مما يحول الأمر إلى فوضى وعدم ترتيب

لهذا وحتى نتخلص من هذه السلبية تم إنشاء متغيرات أطلق عليها اسم الخصائص Properties

وهي تعمل نغس عمل المتغيرات Variables ولكن بمنطق مختلف

لقد تم تصميم عمل الخاصية لجعلها تعمل وفق منطق البواب أو الحارس

فقد تم تضمين الخاصية وظيفتين ضمنيتين يعبر عنهما من خلال كلمات محجوزة مثل get و set

بحيث تعبر الوظيفة set عن الإجراء المسؤول عن إدخال القيم من خارج الصنف إلى داخله

وهو يتم استدعاؤه تلقائيا عند إسناد أي قيمة للخاصية ويتم تمريرها من خلال الكلمة المحجوزة value (وهنا أستخدم مصطلحات لغة C# والتي تكون متشابهة أو قريبة من اللغات الأخرى )

بعد ورود القيمة من خلال المتغير المحجوز يرجع القرار إلى داخل الصنف في قبول تلك القيمة أو رفضها أو القيام بتصرف ما حيال ذلك

فمثلا في خاصية العمر لصنف الشخص

من الممكن أن يمرر المستخدم أي قيمة قد تكون سالبة وربما صفر ومنطقيا هذه القيمة مرفوضة لأنها ليس لها واقع

من هنا ومن خلال بواب الخاصية يقوم بعملية فحص لصلاحية المعطى قبل قبوله وتخزينه في المتغير الداخلي الخاص المسؤول عن تخزين قيمة خاصية العمر

فإن وجده موجبا و ضمن حدود مقبول قبله ووضع قيمة value في المتغير الخلفي

وإلا يرفض ذلك إما بإطلاق استثناء وتوليد خطأ أو إظهار رسالة تشعر بنوع الخطأ الفادح الذي ارتكبه المستخدم

تخيل أنه أدخل قيمة 500 فهل يعقل أن يكون إنسان في عصرنا له عمر 500 إلا إذا كان يستخدم الصنف للحديث عن أنبياء مثل سيدنا نوح عليه السلام فهذا أمر آخر

كذلك الأمر ماذا لو أدخل صفر أو قيمة سالبة حينها من الممكن إظهار رسالة للمستخدم أن العمر لا يجوز أن يكون سالب أو أن العمر يجب ان يكون محصورا بين كذا وكذا

ومن ثم لا يغير شيء في القيمة الحالية للمتغير الخلفي

من هنا نرى أننا قمنا بحماية المتغيرات الداخلية من القيم الخاطئة

من الممكن أن تبرمج القسم set ليقوم بتصحيح تلقائي بدلا من اصدار خطأ

فمثلا لو كانت الخاصية تعبر عن عرض وطول وتم إدخال قيم سالبة أن تجعلها تلقائيا 0 ثم تقبلها

بمعنى لا ترفضها ولكن تحولها إلى أقل قيمة مقبولة لعلمك أن المستخدم لا يقصد قيمة سالبة

أما الوظيفة get فهي المسؤولة عن إخراج قيم المعطيات من داخل الصنف إلى خارجة

وهي عبارة عن دالة ترجع قيمة إلى الخارج

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

إن عملية الإخراج بهذه الطريقة تعطي فوائد جمة

افرض أن لصنف الشخص خاصية تعبر عن تاريخ الميلاد

في هذه الحالة بالإمكان جعل خاصية العمر قيمة محسوبة وليست مخزنة وأن تجعلها للقراءة فقط

نحن قلنا أنك عندما تقرأ الخاصية سيتم استدعاء المنهج الخاص get التابع لتلك الخاصية

من هنا بالإمكان بدلا من إرجاع قيمة من متغير داخلي مخصص للعمر أن تجعله يرجع ناتج عملية طرح التاريخ الحالي من قيمة خاصية تاريخ الميلاد

وبهذا تضمن أنه في أي وقت تقرأ الخاصية ستكون قيمتة محدثة لأنها محسوبة في تلك اللحظة بناء على أحدث قيمة لتاريخ الميلاد

قس عليه الكثير مثل خاصية الطول للمستقيم وخاصية المساحة للمستطيل والدائرة

فإنها كلها ممكن جعلها قيم محسوبة معتمدة على معطيات الكائن من طول وعرض أو نصف قطر

كما بهذه الطريقة بالإمكان جعل الخصائص للقراءة فقط أو للكتابة فقط من خلال إلغاء أو جعل نطاق المناهج خاص بدلا من عام

فمثلا إن تم جعل private set فحينها سيمنع المستخدم من إدخال قيم وإنما فقط القراءة

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

من هنا نرى أنه من الأولى وكتطبيق كامل لمنهج OOP يجب جعل كل منافذ المعطيات عبارة عن خصائص وتجنب استخدام المتغيرات المباشرة والتي تدعى حقول Fields لتمييزها عن الخصائص

قد يقول قائل وهل أنا مضطر لأن أجعل كل متغير بكل هذا التعقيد إذا كان استخدامي له بسيطا

أقول لك لقد أوجدت اللغة حلا وسيطا

بأن جعلت هناك نوعا خاصا من الخصائص هو ذاته الذي تحدثنا عنه ولكنه مختزل

وصيغته مثلا

public int Age { get; set; }

وهذا يعتبر متغير على شكل خاصية أي هو نفسه يمكن تخزين القيمة فيه ولا يحتاج إلى متغير كمخزن خلفي

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

كمثال

public int Age { get; private set; }

يتبع ...

إن شاء الله