التغليف Encapsulation و private و getters و setters

20:00 دقائق مجاني

لماذا نخفي البيانات؟ وما معنى private؟ وكيف نستخدم Getters و Setters لحماية كائناتنا من القيم الخاطئة؟

المشكلة — لماذا نحتاج Encapsulation؟

في الدروس السابقة جعلنا خصائص الكلاس مفتوحة — أي جزء من البرنامج يستطيع تعديلها مباشرة:

⚠️ المشكلة — بيانات مكشوفة
class Person {
    String name;
    int    age;
}

Person p1 = new Person();
p1.age = -100;        ← عمر سالب! Java لن تمنعه
p1.name = "";          ← اسم فارغ! لا أحد يمنعه
😨

بدون Encapsulation

أي مكان في البرنامج يستطيع وضع قيم خاطئة — عمر سالب، اسم فارغ، راتب سالب...

🔒

مع Encapsulation

البيانات محمية — لا يمكن تعديلها إلا بطرق محددة نكتبها نحن مع شروط التحقق.

ما هو Encapsulation؟

التغليف (Encapsulation) هو أحد أعمدة البرمجة الكائنية — ويعني ببساطة: إخفاء البيانات داخل الكلاس والسماح بالوصول إليها فقط بطرق منظّمة.

💬 تشبيه: تخيّل جهاز التلفاز — لا تستطيع لمس دوائره الداخلية مباشرة، بل تتحكم فيه عبر الريموت (زيادة الصوت، تغيير القناة). الكلاس مثل التلفاز، والريموت هو الـ Getters/Setters.
private

إخفاء الخاصية

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

getter

دالة القراءة

دالة تُرجع قيمة الخاصية للقراءة — مثل getName() و getAge().

setter

دالة التعديل

دالة تُعدّل قيمة الخاصية — يمكن فيها وضع شروط تحقق تمنع القيم الخاطئة.

مثال كامل — Person بطريقة احترافية

سنحوّل الكلاس القديم لكلاس محمي بالكامل مع Getters و Setters:

Person.java
class Person {

    // ① الخصائص private — مخفية عن الخارج
    private String name;
    private int    age;

    // ② Getter للاسم — قراءة فقط
    public String getName() {
        return name;
    }

    // ③ Setter للاسم — مع تحقق
    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }

    // ④ Getter للعمر
    public int getAge() {
        return age;
    }

    // ⑤ Setter للعمر — يمنع القيم السالبة
    public void setAge(int age) {
        if (age >= 0) {
            this.age = age;
        }
    }

}
① private

إخفاء الخصائص

لا يمكن لأي كود خارج الكلاس الوصول لـ name و age مباشرة.

② getName()

Getter — دالة قراءة

ترجع قيمة name للخارج بشكل آمن بدون السماح بتعديلها.

③ setName()

Setter — دالة تعديل مع تحقق

يتحقق أن الاسم ليس null وليس فارغاً قبل الحفظ — يرفض الاسم الفارغ صامتاً.

⑤ setAge()

Setter — يمنع العمر السالب

الشرط age >= 0 يضمن أن العمر لن يكون سالباً أبداً.

كيف نستخدم الكلاس المُغلَّف؟

بعد التغليف، تغيّرت طريقة التعامل مع الكائن:

❌ لم يعد مسموحاً

Person p = new Person();
// هذا سيعطي خطأ في التجميع!
p.age  = -100;
p.name = "";

✅ الطريقة الصحيحة

Person p = new Person();
p.setName("Ahmed");
p.setAge(25);
Main.java
Person p = new Person();

// تعديل عبر Setters
p.setName("Ahmed");
p.setAge(25);

// قراءة عبر Getters
System.out.println(p.getName());   ← Ahmed
System.out.println(p.getAge());    ← 25

// محاولة قيمة خاطئة — ستُتجاهل بهدوء
p.setAge(-50);
System.out.println(p.getAge());    ← 25 (لم يتغير)
🖥️
Ahmed
25
25
💡

لاحظ أن setAge(-50) لم يُنفّذ لأن الشرط age >= 0 فشل — العمر بقي 25. هذا هو التغليف في العمل!

اتفاقية التسمية — كيف نسمي Getters و Setters؟

في Java يوجد اتفاقية (Convention) لتسمية Getters و Setters — يجب اتباعها لأن أدوات Java تعتمد عليها:

الخاصية Getter (قراءة) Setter (تعديل)
name getName() setName(String name)
age getAge() setAge(int age)
salary getSalary() setSalary(double salary)
isActive isActive() setActive(boolean val)
💡

للخصائص من نوع boolean، الـ Getter يبدأ بـ is بدل get — مثل isActive() بدل getActive().

لماذا Encapsulation مهم في البرمجة الحقيقية؟

🛡️

حماية البيانات من القيم الخاطئة

الـ Setter يمنع إدخال عمر سالب، راتب صفر، تاريخ غير منطقي — مما يجعل برنامجك أكثر موثوقية وأماناً.

🔧

سهولة الصيانة والتعديل

تغيير قاعدة التحقق يتم في مكان واحد (الـ Setter) فقط — دون تعديل كل الكود الذي يستخدم هذا الكلاس.

📦

فصل المسؤوليات

الكلاس مسؤول عن بياناته — أي كود آخر يستخدمه بدون معرفة التفاصيل الداخلية. هذا هو مبدأ التغليف.

🚀

إضافة منطق بدون تغيير الواجهة

يمكنك لاحقاً إضافة تحقق معقد في الـ Setter (مثل التحقق من قاعدة البيانات) دون أن يتأثر الكود الخارجي بشيء.

أخطاء شائعة عند المبتدئين ⚠️

خطأ ١

نسيان كتابة Getter/Setter ثم التساؤل لماذا لا يعمل

❌ الخطأ

private String name; // نسي كتابة getName() و setName() p.name = "Ahmed"; // خطأ تجميع p.getName(); // خطأ — الدالة غير موجودة
خطأ ٢

Setter بدون أي تحقق — يفقد فائدة التغليف

❌ لا فائدة منه

void setAge(int age) { this.age = age; }

يقبل -100 و -999!

✅ مع تحقق

void setAge(int age) { if (age >= 0) this.age = age; }
خطأ ٣

الخلط بين get و set

getName()

قراءة — ترجع String

setName(String n)

تعديل — تأخذ مدخلاً

📋 ملخص الدرس

private تخفي الخاصية — لا يصل لها أحد من الخارج

Getter = دالة قراءة ترجع القيمة

Setter = دالة تعديل مع شروط تحقق

التسمية: getName() و setName()

🎉 أنجزت أساسيات OOP!

الآن لديك أساس قوي في البرمجة الكائنية: ClassObjectConstructorEncapsulation.

⏭️ في الدروس القادمة:

سنتعلم الثوابت (final)، الكلمة المحجوزة static، المصفوفات، ثم مشروع صغير لتجميع ما تعلمته. 🚀

الدرس التالي العودة للسابق