أساسيات البرمجة الكائنيّة

الحقول والتوابع والحالة

15 دقيقة الدرس 2 من 14

الحقول والتوابع والحالة

في الدرس السابق تعلّمت أن الصنف هو مخطط وأن الكائن هو شيء حقيقي يُبنى من ذلك المخطط. الآن حان الوقت للنظر داخل المخطط وفهم مكوّنيه الأهم: الحقول (ما يعرفه الكائن) والتوابع (ما يستطيع الكائن فعله). يمنح الحقول والتوابع معًا كل كائن حالته الخاصة وسلوكه المميز.

ما المقصود بالحالة؟

الحالة ببساطة هي مجموعة قيم البيانات التي يحتفظ بها الكائن في أي لحظة. تخيّل حسابًا بنكيًا: يمتلك دائمًا رصيدًا واسم مالك ورقم حساب. تلك هي حقوله، وقيمها الحالية تُشكّل حالته. كلّ حسابين كائنان منفصلان بحالتين منفصلتين — تغيير أحدهما لا يمسّ الآخر أبدًا.

حقول النسخة (Instance Fields)

الحقل المُعلَن مباشرةً داخل الصنف خارج أي تابع يُسمّى حقل نسخة. يحصل كل كائن على نسخته المستقلة من كل حقل نسخة.

public class BankAccount { // حقول النسخة — لكل كائن نسخته الخاصة String ownerName; double balance; String accountNumber; }

لمشاهدة تلك النسخ المستقلة عمليًا، أنشئ كائنين وغيّر أحدهما:

public class Main { public static void main(String[] args) { BankAccount alice = new BankAccount(); alice.ownerName = "Alice"; alice.balance = 500.0; BankAccount bob = new BankAccount(); bob.ownerName = "Bob"; bob.balance = 1200.0; // تغيير رصيد bob لا يمسّ رصيد alice bob.balance = 900.0; System.out.println(alice.ownerName + ": " + alice.balance); // Alice: 500.0 System.out.println(bob.ownerName + ": " + bob.balance); // Bob: 900.0 } }
فكرة أساسية — كل كائن مستقل. alice وbob منطقتان مستقلتان في الذاكرة. الحقل balance موجود في كلتيهما، لكنهما متغيّران مختلفان تمامًا. هذه هي جوهر البرمجة الكائنية.

توابع النسخة (Instance Methods)

التابع هو كتلة كود مُسمّاة تنتمي إلى الصنف وتعمل على كائن محدد واحد. عند استدعاء تابع على كائن ما، تُمرّر Java الكائن نفسه إلى التابع بشكل خفي — وهذا ما يُعرف بـمرجع الكائن الضمني (ستتعرّف على صيغته الصريحة this في الدرس التالي؛ اعلم الآن فحسب أنه موجود).

public class BankAccount { String ownerName; double balance; // تابع نسخة — يقرأ حالة الكائن المُستدعَى عليه ويُعدّلها public void deposit(double amount) { balance = balance + amount; // 'balance' هنا تعني رصيد هذا الكائن تحديدًا } public void withdraw(double amount) { if (amount <= balance) { balance = balance - amount; } else { System.out.println("Insufficient funds."); } } public void printSummary() { System.out.println(ownerName + " — Balance: $" + balance); } }

الآن يضمّ الصنف البيانات والعمليات التي تنتمي إلى تلك البيانات:

public class Main { public static void main(String[] args) { BankAccount alice = new BankAccount(); alice.ownerName = "Alice"; alice.balance = 0.0; alice.deposit(500.0); alice.deposit(200.0); alice.withdraw(100.0); alice.printSummary(); // Alice — Balance: $600.0 } }

مرجع الكائن الضمني

حين تكتب alice.deposit(500.0) يقوم Java بشيئين: يبحث عن التابع deposit في صنف BankAccount، ثم يُمرّر alice خفيةً كائنًا مستهدفًا. داخل deposit، كل اسم حقل مكشوف مثل balance هو اختصار لـbalance هذا الكائن. لهذا السبب ينتج عن استدعاء نفس التابع على كائنين مختلفين نتيجتان مختلفتان — كود التابع مشترك، لكنه يُنفَّذ في مواجهة حالة مختلفة في كل مرة.

BankAccount alice = new BankAccount(); alice.ownerName = "Alice"; alice.balance = 0.0; BankAccount bob = new BankAccount(); bob.ownerName = "Bob"; bob.balance = 0.0; alice.deposit(300.0); // تتغيّر alice.balance فحسب bob.deposit(800.0); // تتغيّر bob.balance فحسب alice.printSummary(); // Alice — Balance: $300.0 bob.printSummary(); // Bob — Balance: $800.0
القيم المُعادة من التوابع. يمكن للتوابع إعادة بيانات أيضًا. استخدم نوع الإعادة المناسب بدلًا من void:
public double getBalance() { return balance; }
استدعاء double amount = alice.getBalance(); يسترجع القيمة دون طباعتها، وهو أكثر فائدة للحسابات.

تطوّر الحالة عبر الزمن

الجمع بين الحقول والتوابع يعني أن حالة الكائن تتطوّر مع كل استدعاء تابع. هذا هو السلوك ذو الحالة — وهو ما يُميّز الكائنات عن هياكل البيانات البسيطة. تخيّل استدعاء deposit عشر مرات: ينمو balance عشر مرات، غير أن كائن الحساب يبقى نفس الكائن في الذاكرة بنفس المرجع.

الحقول مقابل المتغيّرات المحلية

كثيرًا ما يخلط المبتدئون بين حقول النسخة والمتغيّرات المحلية. إليك الفروق الأساسية:

  • حقول النسخة تُعلَن داخل الصنف خارج كل التوابع. تعيش طالما يعيش الكائن وتحتفظ بالحالة بين استدعاءات التوابع.
  • المتغيّرات المحلية تُعلَن داخل تابع. توجد فقط طوال مدة ذلك الاستدعاء وتختفي فور انتهاء التابع.
public class Counter { int count; // حقل نسخة — يستمر بين الاستدعاءات public void increment() { int step = 1; // متغيّر محلي — يعيش داخل هذا التابع فحسب count = count + step; } }
خطأ شائع: نسيان تهيئة الحقول. في Java، حقول النسخة من الأنواع العددية تُهيَّأ افتراضيًا بـ0، والمنطقية بـfalse، والكائنات بـnull. الاعتماد الصامت على تلك القيم الافتراضية قد يتسبّب لاحقًا في NullPointerException. اجعل القيم الابتدائية صريحة قدر الإمكان.

الخلاصة

تخزّن حقول النسخة حالة الكائن — يحصل كل كائن على نسخته المستقلة. تعمل توابع النسخة على تلك الحالة؛ عند استدعائها تتلقّى مرجعًا ضمنيًا إلى الكائن المحدد الذي تعمل عليه. تحوّل الحقول والتوابع معًا الصنف من حاوية بيانات سلبية إلى كيان فعّال مكتفٍ بذاته. في الدرس التالي ستتعلّم كيف تتيح لك الدوال البانية (constructors) إعداد تلك الحالة الابتدائية لحظة إنشاء الكائن.