تعلّم البرمجة بلغة كوتلن (25): التغليف Encapsulation

تعلّم البرمجة بلغة كوتلن (25): التغليف Encapsulation
أستمع الى المقال

شرحنا في دروس سابقة، مفهوم الكائنات Objects في البرمجة عمومًا، وكيف يمكننا إنشاء أصناف Classes خاصة بنا في كوتلن، وما هو الباني Constructor وكيفية التعامل معه في كوتلن. في هذا الدرس، سنواصل رحلتنا في طريق فهم البرمجة الكائنية، لنشرح مفهوم آخر مهم جدًا، ألا وهو التغليف Encapsulation، وتطبيقاته في كوتلن.

ما هو التغليف Encapsulation:

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

والآن يريد هؤلاء المبرمجون أن تبقى شفرة مكتبتك على حالها. ﻷن أي تحديث أو تغيير في شفرة المكتبة، قد يؤدي إلى ضرر كبير بشفرتهم التي تعتمد على هذه المكتبة. وأنت كمالك للمكتبة البرمجية، تريد بكل بساطة إجراء عملية إعادة هيكلة Refactoring لشفرتك، ﻷنك ترى أن هناك طريقة أفضل مما كتبته سابقًا.

سيكون الحل الأمثل إذا كنت تستخدم طريقة البرمجة الكائنية في شفرتك، هو معرفة ما هي الأجزاء من الشفرة التي ستسمح لمن يستخدم مكتبتك أن يراها ويستخدمها، وما هي الأجزاء التي ستخفيها عنه. أي بمعنى، عند كتابتة شفرتك، يجب أن تفكر بـ:

فصل أجزاء الشفرة التي ستعمل على تغييرها في المستقبل عن الأشياء التي يجب أن تبقى كما هي.

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

محددات الوصول Access Modifiers:

توفر كوتلن وغيرها من لغات البرمجة التي تعتمد البرمجة الكائنية، محددات تقيد الوصول إلى بعض أجزاء شفرتنا. مثل: public, private, protected, internal. يتم كتابة هذه المحددات قبل أسماء المتغيرات، أو الدوال أو الخاصيّات داخل الأصناف، وأيضًا حتى الاصناف Classes يمكن تقييد رؤيتها باستخدام محددات الوصول.

في هذا الدرس، سنكتفي بشرح محددي الوصول public, private، وسنعود لشرح protected, internal لاحقًا في هذه الدورة.

محدد الوصول private:

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

كمثال، إذا كان لدينا صنف يُمثل الإضاءة Light، ولديه خاصيّة واحدة تعبّر عن حالة الإضاءة lightIsOn. ستكون قيمة هذه الخاصية true إذا كانت الإضاءة تعمل، و false إذا كانت الإضاءة مطفأة. ولدى الصنف أيضًا، دالتين سيستدعيهما من يستخدم هذا الصنف، في إطفاء وإشعال الإضاءة، كالتالي:

نلاحظ أننا أعلنا عن الخاصية lightIsOn بالكلمة var. ﻷننا نحتاج أن نغير قيمتها حسب المعطيات داخل الصنف فقط. المشكلة أنه يمكن تغيير قيمتها في أي مكان داخل البرنامج. ولحل هذه المشكلة وجعل الخاصيّة غير مرئية لمن يستخدم الصنف في مكان آخر غير داخل الصنف، وضعنا الكلمة private قبل اسم الخاصيّة.

وستكون مهمة الدالة ()turnOn عندما يستدعيها المستخدم، هي أن تغير قيمة الخاصيّة lightIsOn إلى القيمة true، ومن ثم طباعة جملة “Light turned on.”. أما دالة ()turnOff، ستكون مهمتها تغيير قيمة الخاصيّة lightIsOn إلى القيمة false، ومن ثم طباعة جملة “Light turned off.”. كلا الدالتين لا تعيدان قيمة معينة.

عند إنشاء كائن من الصنف Light، يمكننا استدعاء إحدى الدالتين ()turnOn أو ()turnOff، كالتالي:

أما الخاصيّة lightIsOn فلا يمكن استدعاؤها ﻷنها محمية بالكلمة private. كما يظهر من الصورة التالية من برنامج IntelliJ:

هكذا نكون قد ضمنا بأنه لن يتم تغيير قيمة الخاصيّة lightIsOn خارج الصنف. 

محدد الوصول public:

كما نرى في الصورة أعلاه، يمكن للمستخدم استدعاء الدالتين خارج الصنف بدون ظهور أي خطأ. وهذا ﻷننا تركنا الوصول إليهما عام public. وبالرغم من عدم كتابة الكلمة public صراحةً قبل اسمي الدالتين، ستضع لهما كوتلن المحدد public. وهذا ما سيحدث لكل الخاصيّات والدوال داخل الصنف، إذا لم نضع لهما محدد وصول معين كـ private مثلًا، تعتبر public مباشرةً.

الـ Setters و الـ Getters في كوتلن:

الـ Setters:

حينما ينشئ المستخدم (المبرمج) كائن من الصنف Light، ويستدعي دالة ()turnOn أو دالة ()turnOff، سيتم طباعة جملتي الطباعة مباشرة. لأن دالة الطباعة موجودة بداخل الدالتين في صنف Light.

هذه الطريقة خاطئة، لماذا؟ لأن المستخدم للصنف قد يريد أن يستخدم لغة أخرى كاللغة العربية مثلاً. ونحن هكذا نجبره على الطباعة باللغة الإنجليزية فقط. لذا سنحذف دالتي الطباعة من داخل دوال الصنف، وسندع المستخدم يقرر كيفية الطباعة بنفسه.

عند إنشاء المستخدم لكائن من الصنف Light، سيحتاج إلى معرفة ما هي قيمة الخاصيّة lightIsOn، ليضع جملة حسب حالة الإضاءة. ولكنه لن يستطيع الوصول إليها ﻷننا وضعنا لها محدد وصول private. لذا سيكون من الأفضل إذا أعطينا المستخدم لصنفنا، قابلية قراءة قيمة الخاصيّة فقط، مع منعه من تعديل قيمتها، فمهمة تعديل قيمة الخاصيّة يجب أن تبقى داخل الصنف Light فقط.

 لفعل ذلك، سنحذف محدد الوصول private من يسار الخاصيّة، وسنضع تحت سطر الخاصيّة مباشرةً، الكلمة المفتاحية set مسبوقة بمحدد وصول private.

وسيكون شكل الصنف بعد التعديل، كالتالي:

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

في واقع الأمر، كان يمكننا استخدام val في الخاصيّة بدل var وحذف الكلمتين private set. ﻷنها في هذه الحالة أيضًا ستكون قابلة للقراءة فقط وغير قابلة للتعديل. ولكن عند استخدام val، نحن أيضًا لن نستطيع تغيير قيمة الخاصيّة داخل الصنف. لذا الطريقة الأسلم كانت هي ما فعلناه أعلاه.

والآن يمكننا إنشاء كائن من الصنف والتصرف حسب القيمة التي تحملها الخاصيّة، كالتالي:

بعد إنشاء كائن من الصنف Light وإسناده إلى bedRoomLight، استدعينا الخاصية lightIsOn داخل كتلة if الشرطية، ووضعنا معها معامل النفي (!). ما سينتجه لنا معامل النفي هو عكس قيمة الخاصيّة. 

وفي المرة الأولى عند بناء الصنف Light، ستكون قيمة الخاصيّة lightIsOn تساوي false. وعكس false يساوي true. وهنا تحقق شرط if، لذا سيتم تنفيذ الشفرة داخلها. داخل كتلة if، نستدعي دالة ()turnOn، والتي ستغير قيمة الخاصيّة lightIsOn إلى true. ثم طباعة الجملة وإنتهاء البرنامج. 

عند تنفيذ البرنامج مرة أخرى، هذه المرة الخاصيّة أصبحت قيمتها true، وعكس true هو false. لذا لن تنفذ كتلة if الشفرة داخلها، وسينتقل التنفيذ مباشرةً إلى ما بداخل كتلة else. ليتم طباعة “إضاءة غرفة النوم تعمل بالفعل” وإنتهاء البرنامج.

الـ Getters:

بعد فترة من الزمن قررنا توفير خاصيّة أخرى تمكن المستخدم من الوصول إلى قراءة قيمة خاصيّة lightIsOn، وإعادة محدد الوصول private إلى خاصيّة lightIsOn. لذا عدلنا على الصنف، كالتالي:

أعلنا عن خاصيّة جديدة اسميناها lightStatus لتعبر عن حالة الإضاءة. وفي السطر التالي مباشرةً، كتبنا الدالة ()get، والتي مهمتها الحصول على قيمة الخاصيّة التي نريد حمايتها lightIsOn.

ما تقوم به ()get، هو إسناد قيمة lightIsOn باستخدام علامة الإسناد (=)، إلى الحقل field أعلاها مباشرةَ. والحقل هنا يقصد به الخاصية الجديدة lightStatus.

الآن يمكن للمستخدم استدعاء الخاصيّة lightStatus، ولن يستطيع استدعاء الخاصيّة lightIsOn ﻷننا اخفيناها باستخدام private.

استخدام private داخل الملف:

يمكننا كتابة المتغيرات خارج الأصناف والدوال ولكن داخل ملف كوتلن والذي ينتهي بالصيغة (kt.). وتسمى متغيرات ذات مستوى عالي Top-Level. وعند استخدام private مع هذه المتغيرات، لا يمكن رؤيتها والوصول إليها إلا داخل الملف الذي توجد به. ونفس الأمر ينطبق على الدوال ذات المستوى العالي Top-Level Functions داخل الملف، والأصناف أيضًا. وهو ما توضحه الصورة التالية:

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

هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.