Getters و Setters في Java: لماذا هم ضروريون حقًا؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: «لماذا لا نجعل الحقول public فقط؟»

\n\n

تخيل موقفًا: أنت تكتب كلاس Person بلغة Java. لديه اسم، عمر، وبريد إلكتروني. أسهل طريقة هي تعريف الحقول على أنها public والوصول إليها مباشرة:

\n\n
public class Person {\n    public String name;\n    public int age;\n    public String email;\n}
\n\n

«إنها تعمل! — ستقول. — لماذا أحتاج إلى getters و setters هذه؟ كود زائد، أسطر تتضخم…»

\n\n

هذا السؤال هو واحد من أكثر الأسئلة شيوعًا على Stack Overflow. آلاف المطورين، خاصة المبتدئين، يتساءلون عنه. الإجابة التي ستحصل عليها من مبرمج Java متمرس ستكون قصيرة: التغليف (Encapsulation). ولكن خلف هذه الكلمة تختبئ فلسفة كاملة في التصميم تنقذ المشاريع من الفوضى.

\n\n

في هذه المقالة، لن نقدم تعريفًا فقط. سنحلل المشكلات الحقيقية التي تحلها getters و setters (وتسمى أيضًا accessors و mutators)، وسنوضح ذلك بكود حي. هيا بنا!

\n\n

1. التغليف: ليس مجرد كلمة رنانة

\n\n

التغليف هو مبدأ من مبادئ البرمجة كائنية التوجه (OOP) ينص على: إخفاء البنية الداخلية للكائن وتوفير واجهة محكومة للتفاعل معه. Getters و Setters هي تلك الواجهة المحكومة.

\n\n

قارن بين النهجين:

\n\n

النهج السيئ (حقول public)

\n
Person person = new Person();\nperson.age = -5; // عمر سالب؟ بسهولة!\nperson.email = "not-an-email"; // بيانات غير صالحة تمر دون تحقق
\n\n

النهج الجيد (getters و setters)

\n
public class Person {\n    private String name;\n    private int age;\n    private String email;\n\n    public int getAge() {\n        return age;\n    }\n\n    public void setAge(int age) {\n        if (age < 0 || age > 150) {\n            throw new IllegalArgumentException("يجب أن يكون العمر بين 0 و 150");\n        }\n        this.age = age;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public void setEmail(String email) {\n        if (email == null || !email.contains("@")) {\n            throw new IllegalArgumentException("بريد إلكتروني غير صحيح");\n        }\n        this.email = email;\n    }\n\n    // باقي الـ getters/setters...\n}
\n\n

الآن، إذا حاول أحدهم تعيين العمر إلى -5، سيقوم البرنامج برمي استثناء. البيانات محمية. ها هي قوة التغليف.

\n\n

2. التحقق من صحة البيانات أثناء التنفيذ

\n\n

Setters هي المكان المثالي للتحقق من صحة البيانات المدخلة. بدونها، سيكون عليك كتابة التحقق في كل مكان تقوم فيه بتعيين قيمة. وهذا يؤدي إلى تكرار الكود وخطر نسيان التحقق في مكان ما.

\n\n

مثال مع حساب بنكي:

\n\n
public class BankAccount {\n    private double balance;\n\n    public double getBalance() {\n        return balance;\n    }\n\n    public void deposit(double amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException("يجب أن يكون مبلغ الإيداع موجبًا");\n        }\n        this.balance += amount;\n    }\n\n    public void withdraw(double amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException("يجب أن يكون مبلغ السحب موجبًا");\n        }\n        if (amount > balance) {\n            throw new IllegalStateException("رصيد غير كافٍ");\n        }\n        this.balance -= amount;\n    }\n}
\n\n

لاحظ: لا يوجد setter مباشر لـ balance. نحن ندير الرصيد من خلال طريقتي deposit و withdraw، اللتين تحتويان بالفعل على كل منطق التحقق. Getter ببساطة يعيد الحالة الحالية.

\n\n

3. المرونة: يمكنك تغيير التنفيذ دون كسر الكود

\n\n

تخيل أنك تخزن العمر في حقل int، ثم قررت تخزين تاريخ الميلاد وحساب العمر ديناميكيًا. إذا كان لديك

المدونات

توصيات الكتب