الوحدة 9: الدوال (Methods) في جافا

تنظيم الكود وإعادة استخدامه بفعالية

المدة: أسبوعان 6 ساعات نظرية 6 ساعات عملية

المقدمة والأهداف التعليمية

لقد قطعنا شوطاً طويلاً في رحلتنا لتعلم البرمجة بلغة الجافا! بعد أن أتقنتَ بناء البرامج التي تتخذ القرارات (جمل الشرط) وتُكرر المهام (الحلقات)، حان الوقت للانتقال إلى مفهوم أساسي لا يقل أهمية: **الدوال (Methods)**. الدوال هي اللبنة التي ستمكنك من كتابة كود منظم، قابل لإعادة الاستخدام، وسهل الصيانة.

في هذه الوحدة، سنتعلم كيف نقوم بتجميع أجزاء من الكود في كتل منطقية قابلة للاستدعاء، وكيف تمرير البيانات إليها، وكيف يمكنها أن ترجع لنا نتائج. هذا المفهوم حيوي لأي مبرمج محترف.

الأهداف:

بنهاية هذه الوحدة، ستكون قادرًا على:

  • فهم مفهوم الدوال (Methods) وأهميتها في تنظيم الكود وتقليل التكرار.
  • التعرف على البنية الأساسية لتعريف الدالة في جافا.
  • التمييز بين الدوال التي ترجع قيمة (Non-void Methods) والدوال التي لا ترجع قيمة (void Methods).
  • تعريف واستخدام الدوال التي لا تستقبل باراميترات.
  • تعريف واستخدام الدوال التي تستقبل باراميترات متعددة من أنواع بيانات مختلفة.
  • استخدام جملة return لإرجاع القيم من الدوال.
  • استدعاء الدوال واستخدام القيم المرجعة منها بفعالية.
  • حل المشكلات البرمجية عن طريق تقسيمها إلى دوال أصغر وأكثر قابلية للإدارة.
---

أولاً: مفهوم الدوال (Methods) في جافا

الدالة (Method)، والتي يطلق عليها أحيانًا "وظيفة" أو "روتين" في لغات برمجة أخرى، هي عبارة عن **كتلة منظمة من التعليمات البرمجية تؤدي مهمة محددة**. تخيلها كـ "صندوق أسود" يأخذ مدخلات معينة، يُجري عليها بعض العمليات، وربما يُنتج مخرجات (نتائج).

المدخلات التي تستقبلها الدالة تسمى **باراميترات (Parameters)**، والنتيجة التي تُعيدها تسمى **قيمة مرجعة (Return Value)**. هذه الأوامر لا تُنفّذ إلا إذا قمنا بـ **استدعاء (Calling)** الدالة بالاسم الذي عرفناها به.

لماذا تُعد الدوال ضرورية جدًا في البرمجة؟
  • **إعادة استخدام الكود (Code Reusability):** هذه هي الفائدة الأهم. بدلاً من كتابة نفس الأوامر عدة مرات في أجزاء مختلفة من برنامجك، يمكنك كتابتها مرة واحدة داخل دالة. بعد ذلك، يمكنك استدعاء هذه الدالة في أي مكان تريده، مما يوفر الوقت ويقلل من حجم الكود بشكل كبير.
  • **تنظيم البرنامج (Program Organization):** تقسيم البرنامج الكبير إلى دوال صغيرة، كل دالة تؤدي وظيفة محددة ومنفصلة، يجعل الكود العام أكثر تنظيمًا، وأسهل في القراءة، والفهم، والصيانة. يُصبح البرنامج ككل عبارة عن مجموعة من المهام المتخصصة التي تعمل معًا.
  • **تقليل الأخطاء (Reduced Errors) وسهولة التصحيح (Debugging):** بما أن الكود المكرر يُستبدل بدالة واحدة، فإن أي تعديل أو إصلاح خطأ يتم في مكان واحد فقط (داخل الدالة). هذا يقلل من فرص حدوث الأخطاء ويجعل عملية اكتشافها وتصحيحها (Debugging) أسهل بكثير، لأنه إذا كان هناك خطأ، فمن المرجح أن يكون موجودًا داخل دالة معينة.
  • **سهولة التعاون (Collaboration):** عندما يعمل عدة مبرمجين على مشروع واحد، يمكن لكل منهم التركيز على تطوير دوال معينة دون التأثير على عمل الآخرين، طالما أنهم يتفقون على واجهة (Interface) الدوال (اسم الدالة، مدخلاتها، ومخرجاتها).

أمثلة على دوال جاهزة في جافا:

لغة جافا، مثلها مثل معظم لغات البرمجة، تحتوي على الكثير من الدوال الجاهزة (Built-in Methods) التي يمكن استخدامها مباشرةً دون الحاجة لتعريفها. هذه الدوال هي جزء من المكتبات القياسية للغة، وتوفر وظائف شائعة. على سبيل المثال، دوال الطباعة التي استخدمناها سابقًا هي في الحقيقة دوال جاهزة:

// دالة print() تطبع النص بدون إضافة سطر جديد بعده
System.out.print("هذا نص سيتم طباعته على نفس السطر."); 

// دالة println() تطبع النص ثم تنتقل إلى سطر جديد
System.out.println("هذا نص آخر مع سطر جديد بعده.");   

// دالة printf() تطبع النص مع تنسيقات محددة (مثل الأرقام والنصوص)
// %d لتنسيق الأعداد الصحيحة، %s لتنسيق النصوص، %n لسطر جديد
System.out.printf("الرقم: %d، النص: %s%n", 123, "مثال على printf");
ملاحظة:

هذه الدوال هي جزء من كائنات (Objects) مثل System.out. ستفهم هذا المفهوم بشكل أعمق بكثير في الوحدات المتقدمة حول البرمجة الكائنية (Object-Oriented Programming).

---

ثانيًا: بناء الدوال في جافا

عند تعريف أي دالة جديدة في جافا، نستخدم بنية محددة تخبر المترجم (Compiler) عن كيفية عمل هذه الدالة وما تتوقعه من مدخلات وماذا تُرجع كمخرج. فهم هذه البنية أمر بالغ الأهمية.

البنية العامة لتعريف الدالة (Method Signature):

modifier returnType methodName(Parameter List) {
    // جسم الدالة (Method Body) - الأوامر التي تنفذها الدالة
    // ...
    // [return value;]  // جملة اختيارية إذا كانت الدالة ترجع قيمة
}

شرح مكونات تعريف الدالة:

  • **modifier (المعدّل):** يحدد طريقة الوصول إلى الدالة (من أين يمكن استدعاؤها) وخصائصها الأخرى. أمثلة شائعة:
    • public: هذا المعدّل يعني أن الدالة يمكن الوصول إليها (استدعاؤها) من أي كلاس آخر في برنامجك.
    • private: يعني أن الدالة يمكن الوصول إليها فقط من داخل نفس الكلاس الذي عُرفت فيه. (مفهوم متقدم سنتناوله في OOP).
    • static: هذه الكلمة المحجوزة تجعل الدالة مرتبطة بـ **الكلاس نفسه** وليس بكائن معين من الكلاس. لهذا السبب نستخدمها دائمًا مع دالة `main` حاليًا، ولتتمكن الدوال الأخرى التي نعرفها من أن تُستدعى مباشرةً من `main` دون الحاجة لإنشاء كائن. سنتعرف عليه بتفصيل أكبر عند دراسة البرمجة الكائنية (OOP).
  • **returnType (نوع القيمة المرجعة):** يحدد نوع البيانات الذي سترجعه الدالة بعد اكتمال تنفيذها. يمكن أن يكون أي نوع بيانات صالح في جافا (مثل: int، double، String، boolean، أو حتى كائن من كلاس معين).
    • **void:** إذا لم تُرجع الدالة أي قيمة على الإطلاق (أي أن وظيفتها هي مجرد تنفيذ أوامر مثل الطباعة أو تغيير حالة شيء ما دون إعطاء نتيجة مباشرة)، نكتب الكلمة المحجوزة void كنوع إرجاع.
  • **methodName (اسم الدالة):** هو الاسم الذي تستخدمه لاستدعاء الدالة في كودك. يجب أن يكون اسمًا معبرًا (Descriptive) عن وظيفة الدالة بحيث يسهل فهم ما تفعله (مثال: `calculateSum` لحساب المجموع، `printReport` لطباعة تقرير، `getUserInput` للحصول على إدخال المستخدم). يفضل أن تبدأ أسماء الدوال بحرف صغير وتتبع نمط "camelCase" (مثال: `myNewMethod`).
  • **Parameter List (قائمة الباراميترات):** هي المدخلات أو القيم التي تستقبلها الدالة عند استدعائها. تحدد أنواع وعدد المتغيرات التي تحتاجها الدالة لأداء وظيفتها.
    • كل باراميتر يتكون من **نوع البيانات** ثم **اسم المتغير** (مثال: int age, String name).
    • إذا كانت الدالة لا تستقبل أي مدخلات، نترك القوسين فارغين: ().
    • قائمة الباراميترات اختيارية؛ قد تكون الدالة بلا باراميترات.
  • **`Method Body` (جسم الدالة):** هي مجموعة الأوامر والتعليمات البرمجية الفعلية التي تنفذها الدالة عندما يتم استدعاؤها. يتم كتابة هذا الكود بين الأقواس المعقوفة {}.
---

ثالثًا: أمثلة عملية حول تعريف دوال جديدة في جافا

المثال الأول: دالة لا ترجع قيمة (void)

دالة وظيفتها فقط طباعة رسالة ترحيب بسيطة، ولا تحتاج لإرجاع أي قيمة، لذا يكون نوع الإرجاع void.

public class SimpleMethodExample {

    // تعريف دالة اسمها welcomeMessage
    // المعدّل: public static (مطلوب حالياً للاستدعاء من دالة main)
    // نوع الإرجاع: void (لا ترجع قيمة)
    // قائمة الباراميترات: () فارغة (لا تستقبل مدخلات)
    public static void welcomeMessage() {
        System.out.println("Welcome to Java Programming!");
    }

    public static void main(String[] args) {
        // استدعاء الدالة welcomeMessage()
        welcomeMessage(); 
        System.out.println("مرحباً بك في الوحدة التاسعة!");
    }
}

🔹 عند التنفيذ سيكون الناتج:

Welcome to Java Programming!
مرحباً بك في الوحدة التاسعة!
تذكير هام:

تذكر أن دالة main هي نقطة البداية لتنفيذ أي برنامج جافا. من داخلها، يمكننا استدعاء الدوال الأخرى التي نقوم بتعريفها.

المثال الثاني: دالة ترجع قيمة (int)

دالة وظيفتها جمع عددين صحيحين وإرجاع ناتج الجمع. بما أنها تُعيد قيمة عدد صحيح، سيكون نوع الإرجاع int.

public class SumMethodExample {

    // تعريف دالة اسمها sum
    // المعدّل: public static
    // نوع الإرجاع: int (ترجع عددًا صحيحًا)
    // قائمة الباراميترات: (int a, int b) تستقبل عددين صحيحين
    public static int sum(int a, int b) {
        int result = a + b; // تقوم بجمع العددين
        return result; // إرجاع النتيجة
    }

    public static void main(String[] args) {
        // استدعاء الدالة sum() مع تمرير القيم 10 و 5
        // وتخزين القيمة المرجعة في المتغير 'total'
        int total = sum(10, 5); 
        System.out.println("10 + 5 = " + total); // طباعة النتيجة المخزنة

        // يمكن أيضاً استخدام القيمة المرجعة مباشرةً في تعبير الطباعة
        System.out.println("20 + 7 = " + sum(20, 7));
    }
}

🔹 عند التنفيذ سيكون الناتج:

10 + 5 = 15
20 + 7 = 27

المثال الثالث: دالة بباراميتر من نوع String

دالة تستقبل اسم شخص كنص (String) وتطبع رسالة ترحيب مخصصة له. بما أنها لا ترجع قيمة، نوع الإرجاع هو void.

public class GreetMethodExample {

    // تعريف دالة اسمها greet
    // نوع الإرجاع: void (لا ترجع قيمة)
    // قائمة الباراميترات: (String name) تستقبل نصًا (اسم)
    public static void greet(String name) {
        System.out.println("Hello " + name + ", welcome to Java!");
    }

    public static void main(String[] args) {
        // استدعاء الدالة greet() بأسماء مختلفة لتمريرها كـ parameters
        greet("Ahmed");
        greet("Sara");
        greet("Student");
    }
}

🔹 عند التنفيذ سيكون الناتج:

Hello Ahmed, welcome to Java!
Hello Sara, welcome to Java!
Hello Student, welcome to Java!

المثال الرابع: دالة ترجع قيمة من نوع double

دالة لتحويل درجة الحرارة من مقياس فهرنهايت إلى مقياس مئوي. بما أن درجة الحرارة يمكن أن تكون كسرية، فإن نوع الإرجاع سيكون double.

public class TemperatureConverter {

    // تعريف دالة اسمها toCelsius
    // نوع الإرجاع: double (ترجع رقمًا عشريًا مزدوج الدقة)
    // قائمة الباراميترات: (double fahrenheit) تستقبل درجة حرارة بالفهرنهايت
    public static double toCelsius(double fahrenheit) {
        // صيغة التحويل من فهرنهايت إلى مئوية
        double celsius = (fahrenheit - 32) * 5 / 9;
        return celsius;
    }

    public static void main(String[] args) {
        // استدعاء الدالة toCelsius() وتخزين القيمة المرجعة
        double tempFahrenheit = 98.6;
        double tempCelsius = toCelsius(tempFahrenheit);
        System.out.println("Temperature " + tempFahrenheit + " Fahrenheit in Celsius: " + tempCelsius);

        // مثال آخر مباشرة دون تخزين في متغير
        System.out.println("Temperature 32.0 Fahrenheit in Celsius: " + toCelsius(32.0)); // درجة تجمد الماء
    }
}

🔹 عند التنفيذ سيكون الناتج:

Temperature 98.6 Fahrenheit in Celsius: 37.0
Temperature 32.0 Fahrenheit in Celsius: 0.0
---

رابعًا: القيم المرجعة (Return Values) واستخداماتها

القيمة المرجعة هي **النتيجة التي تُعيدها الدالة إلى الجزء الذي استدعاها** بعد اكتمال تنفيذها. عندما تحدد نوع قيمة مرجعة غير void لدالتك (مثل int، String، double، إلخ)، فهذا يعني أن الدالة ستقوم بحساب قيمة معينة أو جلبها، ثم تُعيدها لتكون متاحة للاستخدام في مكان الاستدعاء.

جملة return هي الكلمة المحجوزة التي تُستخدم لإرجاع القيمة من الدالة. يجب أن يتوافق نوع القيمة المُرجعة مع returnType المحدد في تعريف الدالة. بمجرد أن يصل تنفيذ الدالة إلى جملة `return`، تتوقف الدالة عن العمل وتعيد القيمة المحددة.

كيفية استخدام القيم المرجعة:

عند استدعاء دالة ترجع قيمة، يمكننا التعامل مع القيمة المرجعة بعدة طرق:

  • **1. تخزين القيمة في متغير:** هذه هي الطريقة الأكثر شيوعًا ووضوحًا، حيث يتم تخزين النتيجة التي أعادتها الدالة في متغير من نفس نوع القيمة المرجعة.
    public class ReturnValueExample {
        public static int multiply(int num1, int num2) {
            return num1 * num2;
        }
    
        public static void main(String[] args) {
            int product = multiply(3, 4); // الدالة multiply سترجع 12
            System.out.println("ناتج الضرب هو: " + product); // الناتج: ناتج الضرب هو: 12
        }
    }
    
  • **2. استخدام القيمة مباشرةً في تعبير أو أمر آخر:** يمكنك استخدام القيمة المرجعة مباشرةً في جملة طباعة، أو في جزء من تعبير حسابي، أو كجزء من شرط، دون الحاجة لتخزينها في متغير مؤقت.
    public class ReturnValueDirectUse {
        public static String getFullName(String firstName, String lastName) {
            return firstName + " " + lastName;
        }
    
        public static void main(String[] args) {
            // استخدام القيمة المرجعة مباشرة في الطباعة
            System.out.println("الاسم الكامل: " + getFullName("محمد", "علي"));
            // الناتج: الاسم الكامل: محمد علي
    
            // استخدام القيمة المرجعة في شرط
            if (getFullName("محمد", "علي").length() > 10) {
                System.out.println("الاسم الكامل طويل.");
            }
        }
    }
    
  • **3. تمرير القيمة المرجعة كباراميتر لدالة أخرى:** يمكن أن تكون القيمة التي تُرجعها دالة هي المدخل (الباراميتر) لدالة أخرى.
    public class ReturnValueAsParameter {
        public static int add(int x, int y) {
            return x + y;
        }
    
        public static void printSum(int sum) {
            System.out.println("المجموع هو: " + sum);
        }
    
        public static void main(String[] args) {
            // ناتج add(5, 7) (وهو 12) يتم تمريره كباراميتر لدالة printSum
            printSum(add(5, 7)); // الناتج: المجموع هو: 12
        }
    }
    
ملاحظة هامة حول void:

إذا كان نوع الإرجاع للدالة هو void، فهذا يعني أنها لا ترجع أي قيمة على الإطلاق. وبالتالي، لا يمكنك:

  • استخدام جملة return مع قيمة (فقط return; لإنهاء الدالة مبكرًا).
  • تخزين ناتج استدعائها في متغير.
  • استخدامها كجزء من تعبير حسابي أو شرط.
وظيفتها مقتصرة على تنفيذ الأوامر داخل جسمها.

---

خامسًا: الخلاصة

لقد أكملتَ هذه الوحدة بفهم عميق لمفهوم **الدوال (Methods)** في Java، وهي لبنات بناء أساسية لبرامج منظمة وفعالة.

  • تعلمنا أن الدوال هي مجموعات من الأوامر القابلة لإعادة الاستخدام، مما يعزز تنظيم الكود ويقلل الأخطاء ويسهل التعاون.
  • فهمنا البنية الأساسية لتعريف الدالة في Java: modifier returnType methodName(Parameter List) { ... }.
  • ميزنا بين نوعي الدوال:
    • **دوال void:** لا ترجع شيئًا، وتُستخدم فقط لتنفيذ مهمة معينة (مثل الطباعة أو تعديل حالة).
    • **دوال ترجع قيمة:** ترجع نتيجة من نوع بيانات محدد (مثل int, double, String)، ويتم ذلك باستخدام كلمة return.
  • رأينا كيف يمكن للدوال أن تستقبل **باراميترات (مدخلات)** لتكون أكثر مرونة وقابلية للتكيف مع سيناريوهات مختلفة.
  • أدركنا أهمية القيم المرجعة وكيف يمكننا تخزينها في متغيرات، أو استخدامها مباشرةً في التعبيرات، أو تمريرها كباراميترات لدوال أخرى.

الدوال هي العمود الفقري لأي برنامج جافا كبير ومعقد، وإتقانها سيفتح لك آفاقًا جديدة في تطوير البرمجيات. تذكر أن الممارسة هي المفتاح! في الوحدة القادمة، سنتعلم عن **المصفوفات (Arrays)**، وهي هياكل بيانات تسمح لنا بتخزين مجموعات من القيم من نفس النوع بكفاءة.

---

سادسًا: تمارين مقترحة 💡

اختبر فهمك لمفهوم الدوال من خلال حل التمارين التالية. حاول كتابة الكود بنفسك قبل البحث عن الحلول!

1. تمرين: دالة لحساب مربع العدد

اكتب دالة اسمها calculateSquare تستقبل عددًا صحيحًا واحدًا (int) كباراميتر، وترجع مربع هذا العدد (int). ثم استدعِ هذه الدالة من دالة main مع عدة أرقام مختلفة واطبع النتائج.

public class SquareCalculator {
    // اكتب الدالة calculateSquare هنا
    // مثال: public static int calculateSquare(int number) { ... }

    public static void main(String[] args) {
        // استدعِ الدالة calculateSquare واطبع النتائج
        // مثال: System.out.println("مربع العدد 5 هو: " + calculateSquare(5));
    }
}

2. تمرين: دالة رسالة ترحيب مخصصة

اكتب دالة اسمها printGreeting تستقبل اسم شخص (String) وعمره (int) كباراميترين. لا ترجع هذه الدالة أي قيمة (void)، بل تقوم بطباعة رسالة ترحيب مخصصة على الشاشة، مثل:
مرحباً يا [الاسم]، عمرك هو [العمر] سنة.
استدعِ الدالة من main مع بيانات مختلفة.

public class CustomGreeting {
    // اكتب الدالة printGreeting هنا
    // مثال: public static void printGreeting(String name, int age) { ... }

    public static void main(String[] args) {
        // استدعِ الدالة printGreeting
        // مثال: printGreeting("أحمد", 30);
    }
}

3. تمرين: دالة لحساب ناتج القسمة

اكتب دالة اسمها divideNumbers تستقبل رقمين عشريين (double) كباراميترين (المقسوم والمقسوم عليه)، وترجع ناتج قسمة الأول على الثاني (double).
تلميح: فكر في حالة القسمة على صفر وكيف يمكنك التعامل معها داخل الدالة (مثلاً، إرجاع قيمة خاصة مثل Double.NaN إذا كان المقسوم عليه صفرًا، أو طباعة رسالة خطأ. معالجة الاستثناءات ستكون موضوعًا في وحدات متقدمة).

public class DivisionCalculator {
    // اكتب الدالة divideNumbers هنا
    // مثال: public static double divideNumbers(double dividend, double divisor) { ... }

    public static void main(String[] args) {
        // استدعِ الدالة divideNumbers واطبع النتائج
        // مثال: System.out.println("ناتج القسمة: " + divideNumbers(10.0, 2.0));
        // مثال: System.out.println("ناتج القسمة على صفر: " + divideNumbers(5.0, 0.0));
    }
}

4. تمرين تحدي: دالة لحساب متوسط الدرجات

صمم دالة اسمها calculateAverage تستقبل خمس درجات صحيحة (int grade1, int grade2, int grade3, int grade4, int grade5) كباراميترات، وتحسب المعدل العام لهذه الدرجات، ثم ترجع الناتج كقيمة عشرية (double).
استدعِ هذه الدالة من main واطبع المعدل.

public class GradeAverageCalculator {
    // اكتب الدالة calculateAverage هنا
    // مثال: public static double calculateAverage(int g1, int g2, int g3, int g4, int g5) { ... }

    public static void main(String[] args) {
        // استدعِ الدالة calculateAverage واطبع النتائج
        // مثال: System.out.println("متوسط الدرجات هو: " + calculateAverage(85, 90, 78, 92, 88));
    }
}
---

المراجع

  • موقع Oracle Java Documentation.
  • "Java: A Beginner's Guide" – Herbert Schildt.
  • "Head First Java" – Kathy Sierra, Bert Bates.