مقدمة: «لماذا لا نجعل الحقول public فقط؟»
\n\nتخيل موقفًا: أنت تكتب كلاس Person بلغة Java. لديه اسم، عمر، وبريد إلكتروني. أسهل طريقة هي تعريف الحقول على أنها public والوصول إليها مباشرة:
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\n1. التغليف: ليس مجرد كلمة رنانة
\n\nالتغليف هو مبدأ من مبادئ البرمجة كائنية التوجه (OOP) ينص على: إخفاء البنية الداخلية للكائن وتوفير واجهة محكومة للتفاعل معه. Getters و Setters هي تلك الواجهة المحكومة.
\n\nقارن بين النهجين:
\n\nالنهج السيئ (حقول public)
\nPerson person = new Person();\nperson.age = -5; // عمر سالب؟ بسهولة!\nperson.email = "not-an-email"; // بيانات غير صالحة تمر دون تحقق\n\nالنهج الجيد (getters و setters)
\npublic 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\n2. التحقق من صحة البيانات أثناء التنفيذ
\n\nSetters هي المكان المثالي للتحقق من صحة البيانات المدخلة. بدونها، سيكون عليك كتابة التحقق في كل مكان تقوم فيه بتعيين قيمة. وهذا يؤدي إلى تكرار الكود وخطر نسيان التحقق في مكان ما.
\n\nمثال مع حساب بنكي:
\n\npublic 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 ببساطة يعيد الحالة الحالية.
3. المرونة: يمكنك تغيير التنفيذ دون كسر الكود
\n\nتخيل أنك تخزن العمر في حقل int، ثم قررت تخزين تاريخ الميلاد وحساب العمر ديناميكيًا. إذا كان لديك
المدونات
توصيات الكتب
اقرأ أيضًا