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; }
يتبع ...
إن شاء الله