Encapsulation في OOP

يعد encapsulation أحد أهم مبادئ البرمجة كائنية التوجه. يتم استخدامه لحماية البيانات داخل الكائنات، والتحكم في كيفية الوصول إلى تلك البيانات، وتنظيم الكود بطريقة نظيفة وآمنة. بعبارات بسيطة، encapsulation يعني إخفاء التفاصيل الداخلية للكائن والسماح بالوصول إليها فقط من خلال طرق خاضعة للرقابة.

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

مقدمة

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

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

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

ما هو encapsulation في البرمجة كائنية التوجه؟

encapsulation في OOP هو عملية تغليف البيانات والسلوك داخل فئة وتقييد الوصول المباشر إلى بعض أجزاء الكائن. يقرر الclass أي الخصائص والأساليب تكون مرئية من الخارج وأي التفاصيل يجب أن تظل خاصة.

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

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

لماذا encapsulation مهم

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

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

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

encapsulation وحماية البيانات

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

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

encapsulation يحل هذه المشكلة عن طريق جعل خاصية التوازن خاصة أو محمية. يمكن للclass بعد ذلك توفير طرق عامة مثل الإيداع والسحب. يمكن لهذه الطرق التحقق من المبلغ قبل تحديث الرصيد.

يحمي هذا الأسلوب البيانات ويبقي الكائن في حالة صالحة.

معدّلات الوصول في encapsulation

معدّلات الوصول هي كلمات أساسية تُستخدم للتحكم في رؤية الخصائص والأساليب داخل class. وهي تحدد ما يمكن الوصول إليه من خارج الclass، وما يمكن الوصول إليه من داخل الclass فقط، وما يمكن الوصول إليه من خلال الفصول الفرعية.

معدّلات الوصول الأكثر شيوعًا في البرمجة كائنية التوجه هي:

  • عام: يمكن الوصول إلى الخاصية أو الطريقة من أي مكان.

  • خاص: يمكن الوصول إلى الخاصية أو الأسلوب فقط داخل نفس class.

  • محمي: يمكن الوصول إلى الخاصية أو الأسلوب داخل نفس class ومن خلال classes الفرعية.

يعد اختيار معدل الوصول الصحيح جزءًا مهمًا من كتابة كود OOP النظيف. يجب على المطورين تجنب جعل كل شيء عامًا لأنه يكشف الكثير من التفاصيل الداخلية ويقلل التحكم في الكائن.

الأعضاء العامة

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

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

تعد الطرق العامة جزءًا من interface الخارجية للكائن. وهي تحدد كيفية تواصل الأجزاء الأخرى من التطبيق مع الكائن.

الأعضاء الخاصون

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

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

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

الأعضاء المحميون

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

على سبيل المثال، قد تحدد فئة الموظف الأساسية خاصية راتب محمية أو طريقة محمية لحساب المكافآت. يمكن للفئات الفرعية مثل المدير أو المطور استخدام هؤلاء الأعضاء مع إبقائهم مخفيين عن العالم الخارجي.

يجب استخدام الوصول المحمي بعناية. يمكن أن يكون مفيدًا في الوراثة، لكن تعريض الكثير لفصول الأطفال قد يجعل صيانة النظام أكثر صعوبة.

جيتر و سيترز

تعد أدوات Getters وSetters من الأساليب المستخدمة لقراءة وتحديث الخصائص الخاصة أو المحمية. فهي توفر الوصول المتحكم فيه إلى بيانات الكائن.

يقوم المُحضر بإرجاع قيمة الخاصية، بينما يقوم المُحدد بتحديث القيمة. تتمثل فائدة استخدام أدوات الضبط في إمكانية إضافة قواعد التحقق من الصحة قبل تغيير البيانات الداخلية.

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

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

مثال encapsulation في PHP

يوضح المثال التالي كيف يمكن استخدام encapsulation في فئة PHP بسيطة:

class BankAccount
{
    private float $balance = 0;

    public function deposit(float $amount): void
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException('Deposit amount must be greater than zero.');
        }

        $this->balance += $amount;
    }

    public function withdraw(float $amount): void
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException('Withdraw amount must be greater than zero.');
        }

        if ($amount > $this->balance) {
            throw new InvalidArgumentException('Insufficient balance.');
        }

        $this->balance -= $amount;
    }

    public function getBalance(): float
    {
        return $this->balance;
    }
}

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

وهذا يحمي الحساب من العمليات غير الصالحة ويحافظ على ثبات الرصيد.

encapsulation في مشاريع البرمجيات الحقيقية

يتم استخدام encapsulation في العديد من المشاريع البرمجية الحقيقية، خاصة في الأنظمة التي تحتاج إلى قواعد واضحة وسلوك موثوق. وهو شائع في تطبيقات الويب وأنظمة الأعمال وinterfaces برمجة التطبيقات ومنصات التجارة الإلكترونية والأنظمة المصرفية وبرامج المؤسسات.

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

بدلاً من ذلك، يمكن لفئة الطلب توفير أساليب مثل addItem، وapplyDiscount، وmarkAsPaid، وmarkAsShipped. يمكن أن تتضمن كل طريقة قواعد العمل المطلوبة ومنطق التحقق من الصحة.

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

encapsulation وقابلية صيانة الكود

يعمل encapsulation على تحسين قابلية الصيانة لأنه يclass التنفيذ الداخلي للفئة عن الطريقة التي تستخدمها بها الأجزاء الأخرى من التطبيق. عندما تكون التفاصيل الداخلية مخفية، يمكن للمطورين تحديث الclass أو تحسينه دون التأثير على النظام بأكمله.

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

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

encapsulation والكود النظيف

لا يقتصر الكود النظيف على كتابة وظائف قصيرة أو أسماء قابلة للقراءة فقط. يتعلق الأمر أيضًا بالتحكم في المسؤوليات وتقليل التبعيات غير الضرورية بين الأجزاء المختلفة من التطبيق.

يدعم encapsulation الكود النظيفة عن طريق تقييد الوصول المباشر إلى البيانات وكشف السلوكيات ذات المعنى فقط. بدلاً من السماح للتعليمات البرمجية الخارجية بتغيير الخصائص مباشرةً، توفر class أساليب تصف إجراءات العمل.

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

encapsulation مقابل abstraction

encapsulation وabstraction مفهومان مرتبطان، لكنهما ليسا متماثلين. يركز encapsulation على حماية البيانات والتحكم في الوصول إلى التفاصيل الداخلية. يركز abstraction على إخفاء التعقيد وإظهار السلوك الأساسي فقط.

يجيب encapsulation على السؤال: من يمكنه الوصول إلى هذه البيانات أو الطريقة؟ يجيب abstraction على السؤال: ما الذي يحتاج مستخدم هذا الclass إلى معرفته؟

ومن الناحية العملية، غالبًا ما يعمل كلا المبدأين معًا. قد يقوم الclass بإخفاء بياناته الداخلية باستخدام encapsulation مع توفير واجهة عامة بسيطة من خلال abstraction.

الأخطاء الشائعة في encapsulation

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

خطأ آخر هو إنشاء حروف ومحددات لكل عقار دون تفكير. إذا كانت كل ملكية خاصة تحتوي على أداة عامة ومحددة، فقد لا يزال الclass يكشف الكثير من بنيتها الداخلية.

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

يحافظ encapsulation الجيد على البيانات والسلوكيات ذات الصلة معًا ويكشف فقط عن العمليات المنطقية للكائن.

أفضل الممارسات للتغليف

لاستخدام encapsulation بشكل صحيح، يجب على المطورين تصميم فئات ذات مسؤوليات واضحة. يجب على كل فئة حماية حالتها الداخلية وتوفير طرق مفيدة للتفاعل مع تلك الحالة.

تتضمن أفضل الممارسات المفيدة ما يلي:

  • حافظ على خصوصية الممتلكات أو حمايتها ما لم يكن هناك سبب قوي لجعلها عامة.

  • استخدم الأساليب العامة لتمثيل إجراءات ذات معنى.

  • التحقق من صحة البيانات قبل تغيير الخصائص الداخلية.

  • تجنب الكشف عن التفاصيل الداخلية غير الضرورية.

  • استخدم الحروف فقط عندما يحتاج الكود الخارجي حقًا إلى قراءة قيمة ما.

  • استخدم أدوات الضبط بعناية وقم بتضمين التحقق من الصحة عند الحاجة.

  • أبقِ قواعد العمل قريبة من البيانات التي تتحكم فيها.

تساعد هذه الممارسات في إنشاء فئات يسهل اختبارها وإعادة استخدامها وصيانتها.

لماذا يعتبر encapsulation مهمًا للمبتدئين

بالنسبة للمبتدئين، قد يبدو encapsulation بمثابة قاعدة إضافية تجعل الكود أطول. ومع ذلك، مع نمو التطبيقات، تصبح قيمة encapsulation واضحة.

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

يساعد تغليف التعلم أيضًا المبتدئين على فهم تصميم البرامج الاحترافية. تعتمد معظم الأطر وأنماط التصميم الحديثة على فكرة الوصول المتحكم فيه وسلوك الكائن المحدد جيدًا.

خاتمة

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

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

لأي مطور يتعلم OOP، يعد فهم encapsulation أمرًا ضروريًا. إنه أحد أسس التصميم النظيف الموجه للكائنات وخطوة مهمة نحو كتابة كود احترافية وقابلة للصيانة وقابلة للتطوير.