برمجة

التغليف (Encapsulation) في البرمجة الكائنية

مقدمة عميقة في فلسفة التغليف

التغليف ـ أو Encapsulation ـ يشكِّل أحد الأعمدة الأربعة للبرمجة الكائنية (إلى جانب الوراثة، التعددية الشكلية، والتجريد). الفكرة الجوهرية بسيطة في ظاهرها: جمع البيانات (الحالة) والسلوك (الدوال أو الأساليب) في وحدة منطقية واحدة تُسمَّى «كائنًا» مع منع الكود الخارجي غير المصرَّح له من الوصول المباشر إلى تفاصيل تلك البيانات. غير أنّ تطبيق هذه الفكرة يفتح أبوابًا واسعة أمام جودة التصميم، قابلية الصيانة، الأمان، وإمكانات التطوير المستقبلي للمشروعات البرمجية. منذ ظهورها في لغات مثل ‎Simula-67‎ ثم ‎Smalltalk‎ تطورت تقنيات التغليف حتى أصبحت ركيزة رئيسة لكل لغة حديثة تدعم النمط الكائني، سواء كانت ‎‎C++‎‎ أو ‎Java‎ أو ‎C#‎ أو ‎Python‎ أو ‎Ruby‎ أو ‎JavaScript‎ ES2022.

القسم الأول: الجذور الفكرية والتاريخية للتغليف

1-1 من البرمجة الإجرائية إلى الكائنية

في البرمجة الإجرائية التقليدية كانت البيانات تُمرَّر حرة بين الدوال، ما يجعل الاعتماد المتبادل عاليًا ويصعِّب تتبُّع الأخطاء. لاحظ الباحث د. كريستوفر ستراتشي (Christopher Strachey) مطلع الستينيات أن فصل البيانات عن الدوال يشبه «جسدًا بلا جلد»، ومن هنا أتت محاولات الدمج بينهما لحماية الحالة الداخلية. ظهرت الفكرة عمليًّا في ‎Simula-67‎ عبر مصطلح «الفئة (Class)» التي تُعرِّف متغيّرات خاصة ودوالًا تتحكم في سلوكها. هذه القفزة التاريخية أرست مفهوم «الكائن» كوحدة محكمة لا يُسمح للبيانات فيها بأن تعيش في العراء.

1-2 التطور عبر الأجيال

  • Smalltalk (1972): عمّمت مبدأ «كل شيء رسالة» وجعلت إرسال الرسائل الوسيلة الوحيدة للتفاعل مع الكائن، مغلقةً الحالة تمامًا.
  • C++ (1983): أضافت محدِّدات الوصول (private / protected / public)، ما منح المطوّر سيطرة حبيبية على مستوى الانفتاح أو الإخفاء.
  • Java (1995): رسَّخت مبدأ «اكتب مرة… شغِّل في كل مكان» مع طبقة أمان (JVM) تُنفّذ فحص bytecode للتأكّد من احترام حدود التغليف.
  • ‎Python (1991) ثم ES2022 (حقول ‎#private‎): أثبتت أنّ التغليف ممكن حتى في اللغات الدينامية، عبر قواعد اسمية (name mangling) أو مساحات أسماء معيارية.

القسم الثاني: الأساس النظري للتغليف

2-1 إخفاء المعلومات (Information Hiding)

عرّفه ‎د. ديفيد بارناس‎ (Parnas 1972) بأنّه «الفصل الصارم بين الواجهة والاستخدام»، بحيث يَعرف مستخدم الوحدة ماذا تفعل وليس كيف تفعل. يفضي هذا إلى:

  1. تقليل التشابك (Coupling) بين الوحدات.
  2. زيادة التماسك (Cohesion) داخل الوحدة.
  3. إمكان استبدال الخوارزمية الداخلية دون كسر الكود الخارجي.

2-2 الفرق بين التجريد والتغليف

  • التجريد (Abstraction): إبراز الخصائص الجوهرية وإخفاء التفصيلات البعيدة عن نطاق الاهتمام.
  • التغليف: آلية تنفيذية للحفاظ على ذلك الإخفاء عبر محدّدات الوصول، الواجهات، والكائنات.

2-3 مكونات التغليف الناجح

  • الحقول (Fields): تمثّل «الحالة State».
  • الأساليب (Methods): تعبّر عن «السلوك Behavior».
  • الواجهات (Interfaces): تُحدِّد العقد المتفق عليه بين الكائن والعالم الخارجي.
  • محدّدات الوصول (Access Modifiers): أدوات لضبط الرؤية (Visibility).
  • منشئات ومُحطّمات (Constructors / Destructors): تتحكم في دورة حياة الكائن، مما يضمن سلامة التهيئة والتدمير.

القسم الثالث: مقارنة محدّدات الوصول في لغات مختارة

اللغة خاص (Private) محمي (Protected) عام (Public) داخلي/حزمة (Internal / Package)
C++ private protected public
Java private protected public «حزمة» (بدون كلمة مفتاحية)
C# private protected / protected internal public internal
Python بادئة ‌__ لا يوجد رسميًا أي اسم عادي وحدة (Module)
Ruby private protected public
ES2022 #field لا يوجد عادي وحدة (Module)

جدول 1: مقارنة كلمات الوصول.

القسم الرابع: تطبيقات التغليف في لغات البرمجة الحديثة

4-1 التغليف في ‎C++‎

يدعم ثلاثة مستويات وصول مع إضافة الكلمة المفتاحية friend التي تكسر التغليف عن قصد، ما يستوجب الحذر. يعتمد المصمّمون على نمط «الصنف الغلاف» (Wrapper Class) لعزل المؤشرات الخام وتقليل خطأ «التعليق المعلَّق» (Dangling Pointer).

4-2 التغليف في ‎Java‎

التنفيذ داخل الـ‎JVM‎ يمنع الكود الأصلي (Native) من العبث بالذاكرة مباشرة، ويتيح عاكسًا (Reflection) مضبوط الصلاحيات. ينصح الدليل الرسمي ‎Effective Java‎ باستعمال «حقول خاصة + طرق وصول» واتّباع نمط Immutable Object لتحقيق أمان الخيوط (Thread-Safety).

4-3 التغليف في ‎C#‎ (.NET)

يُقدِّم خاصية ‎properties‎ التي تنشئ تلقائيًا دوال get/set مع دعم السمات (Attributes) والـ partial class لفصل الشيفرة المولَّدة عن المكتوبة يدويًّا دون كسر التغليف.

4-4 التغليف في ‎Python‎

لا يوجد تقييد صارم؛ إلا أنّ الاتفاق المجتمعي يوظّف بادئة ‌_‎ للحماية الناعمة، و‌__name‎ للتشويش (Name Mangling) داخل الفئة. تُوصي ‎PEP 8‎ بعدم الوصول للحقل المبدوء بـ _ خارج الفئة.

4-5 التغليف في ‎Ruby‎

تستعمل ‎attr_reader / attr_writer / attr_accessor‎ لتوليد دوال وصول. الاختلاف أنّ كلمة ‎private‎ تجعل الطريقة خاصة للفئة وابنائها لكن يمكن استدعاؤها دون مُرسِل صريح (Receiverless).

4-6 التغليف في ‎PHP 8‎

منذ الإصدار ‎5‎ يدعم ‎private/protected/public‎، ثم أضيفت خاصية ‎promoted constructor properties‎ لتقليل التكرار. يُراعى ضبط ‎magic methods‎ مثل ‎__get‎ و‎__set‎ كي لا تصبح بوابة خلفية لكسر التغليف.

4-7 التغليف في ‎JavaScript‎ ES2022

قُدّمت الحقول الخاصة ‎#x‎ داخل فئة، تُترجم إلى ‎WeakMaps‎ في الكواليس مما يمنع الوصول عبر ‎this["#x"]‎. بذلك تنهي عقودًا من الاعتماد على ‎closures‎ للتحايل على غياب التغليف.

القسم الخامس: الأنماط التصميمية المرتبطة بالتغليف

  1. Facade: يغلِّف مجموعةً من الأنظمة الفرعية خلف واجهة مُبسَّطة.
  2. Adapter: يغلِّف واجهة كائن لتركيبها مع واجهة متوقَّعة.
  3. Decorator: يغلِّف كائنًا لإضافة سلوكيات وقت التشغيل دون توريث.
  4. Factory Method / Abstract Factory: يغلِّف منطق إنشاء الكائنات ويعزل المستهلك عن التفاصيل.
  5. Proxy: يغلِّف الوصول لكائن حقيقي بغرض الكاش أو التفويض أو الأمان.

القسم السادس: الفوائد الاستراتيجية للتغليف

6-1 صيانة أسهل

عند اكتشاف خطأ في خوارزمية داخلية يكفي تعديل الفئة من دون تعديل الأكواد التي تستعملها، لأنّ الواجهة ثابتة.

6-2 تقليل التشابك وقابلية إعادة الاستخدام

الاعتماد الضئيل (Loose Coupling) يسمح بإعادة توظيف الفئة في مشروع آخر بإضافة ملفات المصدر فقط.

6-3 الأمان وسلامة النوع

منع التلاعب المباشر بالذاكرة (كما في Java) يقلّل هجمات Buffer Overflow، ويحدّ من حالات اختراق منطق الأعمال (Business Logic).

6-4 دعم البرمجة المتزامنة

البيانات المحمية لا تُعدَّل إلا عبر أساليب متحكَّم فيها يمكن أن تحتوي على أقفال (Locks)، ما يقلّل سباقات البيانات (Data Races).

6-5 اختبار أسهل

بتحديد واجهة صغيرة يمكن استعمال Mock Objects لاختبار الوحدة (Unit Testing) دون الحاجة إلى المكوّنات الحقيقية المُعقَّدة.

القسم السابع: التحديات وكسر التغليف

  1. الانعكاس (Reflection): أدوات مثل ‎java.lang.reflect‎ أو ‎setAccessible(true)‎ قد تكسر الحدود الأمنية إن أُسيء استعمالها.
  2. الأداء: الاستدعاء عبر دوال get/set قد يضيف طبقة تكلفة طفيفة مقارنة بالوصول المباشر في لغات مُجمَّعة.
  3. قابلية الاختراق في اللغات الدينامية: المطوّر غير المنضبط يمكنه تغيير حقل خاص باستخدام ‎__dict__‎ في Python أو ‎Object.defineProperty‎ في JavaScript.
  4. عائق التعلّم للمبتدئين: فهم «الخلفية» مقابل «الواجهة» يتطلّب عقلية تصميمية تختلف عن البرمجة الخطية.

القسم الثامن: التغليف المتقدم

8-1 الكائنات غير القابلة للتغيير (Immutable Objects)

عند جعل كل الحقول خاصة ونهائية (final) تُنشأ الكائنات تامة الحالة؛ أي أي محاولة تعديل تُعيد كائنًا جديدًا. هذا النمط شائع في ‎Scala‎ و‎Kotlin‎ ويساهم في البرمجة المتوازية.

8-2 وحدات البرمجيات النمطية (Modular Encapsulation)

  • Java Platform Module System (JPMS): يتيح كلمة ‎exports‎ و‎opens‎ للتحكم في حزم مرئية أو غير مرئية.
  • ES Modules: يمكن تصدير واستيراد فقط ما يُعلن في ‎export‎، معزِّزًا مبدأ أقلّ امتياز.

8-3 التغليف في البنى الميكروسخدمية (Micro-services)

على مستوى معماري أعلى، تختزل الخدمة API REST أو gRPC تُخفي قواعد بياناتها ومنطقها الداخلي عن بقية الخدمات، ما يُعتَبَر «تغليفًا» على نطاق النظام لا الملف.

8-4 ميتا-برمجة وتعقيد الكائن

أطر مثل AspectJ أو Spring AOP تستطيع حقن تعليمات قبل/بعد استدعاء الأسلوب دون تغيير الشيفرة الأصلية، ما يستوجب جدار حماية Configuration لمنع تجاوز الحدود.

القسم التاسع: دراسات حالة

9-1 مكتبة ‎java.util.ArrayList

أخفت الحقل ‎elementData‎ الخاص، وتتحكم الأساليب في توسيع المصفوفة بمجرد تخطي السعة، ما سمح بترقية الخوارزمية في JDK 8 دون كسر برامج بُنيَت عام 1998.

9-2 مشكلة ‎strcpy‎ في ‎C‎

غياب التغليف والسماح بالوصول إلى مؤشرات الذاكرة مباشرة أدى إلى آلاف الثغرات؛ بينما التحول إلى std::string في ‎C++‎ أنقص الهجمات لأنّ الحقل size مخفي ويمنع الكتابة خارج الحدود.

9-3 إطار عمل ‎React‎ و«الحالة المحلية»

يُعتبَر ‎useState‎ تغليفًا للحالة داخل مكوِّن UI بحيث لا يطلع عليها مكوّن آخر إلا عبر Props، ما يسهل التنبؤ بسلوك التطبيق.

القسم العاشر: أدوات وممارسات لضمان التغليف

  • تحليل ثابت (Static Analysis): أدوات مثل ‎SonarQube‎ تفحص الكود بحثًا عن حقول public غير مبرَّرة.
  • متتبِّع الوصول (Access Tracer): يسجّل حالات الكسر في زمن التشغيل عبر الانعكاس.
  • أدلة الأسلوب (Style Guides): مثل ‎Google C++‎ Style Guide توصي بأن تكون الحقول private افتراضيًا، ولا تُفتح إلا لحاجة.
  • مؤشرات الاقتران (Coupling Metrics): مثل ‎Instability (I)‎ و‎Afferent/Efferent Coupling‎ لقياس مدى احترام التغليف على مستوى الحزمة.

 

المختصر المفيد

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

من المقدمة السابقة يمكن
تعرف Encapsulation بانها وسيلة لاخفاء بيانات الكلاس ومنع التعامل معها بطريقة مباشرة من خارج الكلاس.

السؤال كيف يتم اخفاء بيانات الكلاس ومنع التعامل معها خارج الكلاس ؟Encapsulation In C++

هنا هيظهر دور Access modifiers وبالتحديد private او خاص وبالتالى يتم تحديد طرق الوصول الخاصة بالمتغيرات بانها خاص private . private ويقصد بة انة يمكن الوصول للبيانات فقط من داخل الكلاس .

سؤال ماهى الطريقة غير المباشرة للوصول الى البيانات التى تم اخفائها من خارج الكلاس؟

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

getter & setter عبارة عن دوال
getter تستخدم للحصول على قيمة المتغير
setter. تستخدم لاسناد قيمة للمتغير

هنا يتم اعطاء هذة الدوال صلاحية الوصول لهذة المتغيرات والتعامل معها وعندما نريد التعامل مع المتغيرات من خارج الكلاس يتم من خلال هذة الدوال (بطريق غير مباشر) . وطالما يتم التعامل مع المتغيرات من خلال هذة الدوال . فمن الممكن التحكم او عمل شرط (validation) على القيم المررة للدالة setter قبل حفظها بداخل هذة المتغيرات .

حتى نتمكن من تطبيق مفهوم Encapsulation يتم اولا جعل امكانية الوصول للمتغيرات private وبعدها اذا اردنا اسناد قيمة لاى متغير من خارج الكلاس نستخدم دالة setter اما اذا اردنا الحصول على قيم المتغير نستخدم دالة getter ولو اردنا اسناد والحصول على قيم المتغير نستخدم الدالتين معا .ومن الممكن عمل شرط على هذة البيانات قبل تخزينها بداخل هذا المتغير من خلال الدالة setter .

خاتمة

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

المراجع

  1. Booch, G. Object-Oriented Analysis and Design with Applications, 3rd ed., Addison-Wesley, 2007.
  2. Gamma, E. et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994.
  3. Parnas, D. L. “On the Criteria To Be Used in Decomposing Systems into Modules.” Communications of the ACM 15(12), 1972.
  4. Bloch, J. Effective Java, 3rd ed., Addison-Wesley, 2018.
  5. Stroustrup, B. The C++ Programming Language, 4th ed., Addison-Wesley, 2013.
  6. Fowler, M. Refactoring: Improving the Design of Existing Code, 2nd ed., Addison-Wesley, 2018.
  7. Oracle. Java SE 20 Documentation, 2025.
  8. Microsoft Docs. C# Language Reference, 2025.
  9. Lutz, M. Learning Python, 5th ed., O’Reilly Media, 2013.
  10. ECMAScript® 2022 Language Specification, TC39.

 

اترك تعليقاً

زر الذهاب إلى الأعلى