واحدة من أعظم نقاط القوة في Java هي البرمجة الموجهة للكائنات (OOP). وهذا هو السبب الذي جعل هذه اللغة تحظى بشعبية كبيرة وهي مناسبة تمامًا للمشاريع من أي حجم. ما هي البرمجة الشيئية؟ إنه ليس سحرًا، لكنه قد يبدو سحريًا إذا دخلت فيه حقًا. يدور OOP حول كيفية إنشاء برنامجك. إنه مفهوم، أو بالأحرى مجموعة من مفاهيم oop في Java، يسمح لك بإنشاء بعض التفاعلات والعلاقات المحددة بين كائنات Java من أجل تطوير البرامج واستخدامها بشكل فعال. مفاهيم OOP في جافا - 1تتضمن OOP الكلاسيكية 3 + 1 مفاهيم رئيسية. لنبدأ بالكلاسيكيات.

الكائن

تتميز كائنات Java بالإضافة إلى كائنات العالم الحقيقي بخاصيتين: الحالة والسلوك.

على سبيل المثال، الكائن البشري له حالة (الاسم، الجنس، النوم أم لا...) والسلوك (يدرس جافا، يمشي، يتحدث...). يقوم أي كائن Java بتخزين حالته في الحقول ويكشف سلوكه من خلال الأساليب.

التغليف

يقوم تغليف البيانات بإخفاء البيانات الداخلية عن العالم الخارجي، ولا يمكن الوصول إليها إلا من خلال طرق مكشوفة للعامة. ماذا يعني ذالك؟ ما البيانات؟ يختبئ ممن؟ الإخفاء يعني تقييد الوصول المباشر إلى أعضاء (حقول) البيانات الخاصة بالفئة.

كيف يعمل في جافا:

  1. الحقول مصنوعة خاصة
  2. يحصل كل حقل في الفصل الدراسي على طريقتين خاصتين: المُحضر والمُحدد. تقوم أساليب Getter بإرجاع قيمة الحقل. تتيح لك أساليب الضبط تغيير قيمة الحقل بطريقة غير مباشرة ولكن مسموح بها.

مثال على التغليف في كود Java:

public class Student {
private int age;
private String name;

public int getAge() {
return age;
}

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

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
// The name field is private, so you can no longer do this:  firstStudent.name = "John";
}
}

لماذا يجب عليك استخدام التغليف؟

السبب الرئيسي هو تسهيل تغيير التعليمات البرمجية الخاصة بك. تخيل أن لديك طلبًا لمدرسة للهوكي ويوجد فصل لطلاب الهوكي يحتوي على حقلين يخزنان اسم الطالب وعمره عند تسجيله في المدرسة. شيء من هذا القبيل:
public class HockeyStudent {
public String name;
public  int ageOfEnrollment;
}
حقل ageOfEnrollment عام، ولا توجد حروف أو أدوات ضبط... يتم استخدام هذه الفئة من قبل العديد من الفئات الأخرى، وكان كل شيء على ما يرام حتى قرر أحد المطورين أن حقل int واحد ليس كافيًا. بعض لاعبي الهوكي في المجموعة أكبر من أقرانهم بسنة واحدة تقريبًا، لذلك سيكون من الملائم تقسيمهم إلى مجموعتين اعتمادًا على الشهر الذي ولدوا فيه. لذلك يجب تغيير حقل ageOfEnrollment إلى مصفوفة int (int[][]) : الرقم الأول مخصص للسنوات الكاملة والثاني للأشهر. أنت الآن بحاجة إلى إعادة بناء كافة التعليمات البرمجية التي تستخدم فئة الطالب ! ولكن إذا كان حقل ageOfEnrollment الخاص بك خاصًا وكان لديك حروف الحروف والمحددات، فسيكون كل شيء أسهل. إذا تغيرت متطلبات تحديد عمر الطالب، فما عليك سوى تحديث المنطق في طريقة ضبط setAgeOfEnrollment() ويمكن لفصولك الاستمرار في استخدام الطالب دون أي مشاكل! هذا المثال مفتعل إلى حد ما، لكنني آمل أن يوضح لماذا يعد استخدام التغليف فكرة رائعة.

ميراث

هذا المبدأ أسهل في الفهم حتى بدون أي خبرة عملية. لا تكرر نفسك (DRY) يمكن أن يكون شعار مفهوم الميراث. يتيح لك الوراثة إنشاء فئة فرعية ترث حقول وأساليب الفئة الأصلية دون إعادة تعريفها. بالتأكيد، يمكنك تجاوز حقول وأساليب الفصل الأصلي في الفصل الفرعي، لكن هذا ليس ضروريًا. علاوة على ذلك، يمكنك إضافة حالات وسلوكيات جديدة في الفصل الفرعي. تُسمى الفئات الأصلية أحيانًا بالفئات الفائقة أو الفئات الأساسية، وتُعرف الفئات الفرعية بالفئات الفرعية. يتم استخدام الكلمة الأساسية الممتدة في Java لتنفيذ مبدأ الميراث في التعليمات البرمجية.

كيف يعمل في جافا:

  1. إنشاء الفئة الأصل.
  2. قم بإنشاء فئة فرعية باستخدام الكلمة الأساسية الممتدة .
  3. في مُنشئ فئة الطفل، استخدم الأسلوب super(parentField1,parentField2,...) لتعيين حقول الأصل.

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

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

Student firstStudent = new Student();

يمكنك استخدام الكلمة الأساسية الجديدة لاستدعاء المُنشئ الافتراضي لفصل الطالب : tudent() .

بعض القواعد:

  1. يمكن أن يكون للفصل الدراسي والد واحد فقط.
  2. يمكن أن تحتوي فئة الأصل الواحدة على العديد من الفصول الفرعية.
  3. يمكن أن يكون للفصل الفرعي فصول فرعية خاصة به.

مثال على الميراث في كود جافا

لنقم بإنشاء فئة الهاتف .
public class Phone {
    int price;
    double weight;

// Constructor
public Phone(int price, double weight) {
        this.price = price;
        this.weight = weight;
    }

    void orderPhone(){
        System.out.println("Ordering phone...");
    }
}
بالطبع، هناك أنواع مختلفة من الهواتف، لذلك دعونا ننشئ فئتين فرعيتين: واحدة لهواتف Android والثانية لهواتف iPhone. ثم سنقوم بإضافة بعض الحقول والأساليب التي لا يملكها الأصل. وسنستخدم super() لاستدعاء المُنشئين لتهيئة الحقول الموجودة في الفئة الأصلية.

مثال على الميراث في جاوة

public class Android extends Phone {

// Some new fields
String androidVersion;
int screenSize;

    String secretDeviceCode;

// Constructor
    public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
        super(price, weight); // Android inherits Phone’s fields

        //this - reference to the current object
        //super - reference to the parent object

        this.androidVersion = androidVersion;
        this.screenSize = screenSize;
        this.secretDeviceCode = secretDeviceCode;
    }

	// New Android-specific method, does not exist in the Phone class
    void installNewAndroidVersion() {
        System.out.println("installNewAndroidVersion invoked...");

    }

}

public class IPhone extends Phone {

    boolean fingerPrint;

    public IPhone(int price, double weight, boolean fingerPrint) {
        super(price, weight);
        System.out.println("IPhone constructor was invoked...");
        this.fingerPrint = fingerPrint;
    }

    void deleteIPhoneFromDb() {
        System.out.println("deleteIPhoneFromDb invoked...");
    }

@Override // This is about polymorphism, see below
void orderPhone(){
        System.out.println("Ordering my new iPhone and deleting the old one...");
    }
}
لذا، للتكرار: في Java، يتيح لك الوراثة توسيع الفصل الدراسي بفئات فرعية ترث حقول وأساليب الفصل الأصلي. إنها طريقة ممتازة لتحقيق إمكانية إعادة استخدام التعليمات البرمجية.

تعدد الأشكال

تعدد الأشكال هو قدرة الكائن على التحول، واتخاذ أشكال مختلفة أو بالأحرى التصرف بطرق مختلفة. في Java، يحدث تعدد الأشكال عادةً عند استخدام مرجع فئة الأصل للإشارة إلى كائن فئة فرعية.

ماذا يعني ذلك وكيف يعمل في جافا:

ما هو تعدد الأشكال في جافا؟ بشكل عام، هذا يعني أنه يمكنك استخدام نفس اسم الطريقة لأغراض مختلفة. هناك نوعان من تعدد الأشكال في Java: تجاوز الطريقة (تعدد الأشكال الديناميكي) والتحميل الزائد للطريقة (تعدد الأشكال الساكن).

تجاوز الأسلوب

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

مثال على تعدد الأشكال في كود جافا

public class Musician {
    String name;
    int age;

    // Default constructor
    public Musician() {
    }

    // Parameterized constructor
    public Musician(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void play() {
        System.out.println("I am playing my instrument...");
    }
}
يستخدم الموسيقيون المختلفون أدوات مختلفة. لنقم بإنشاء فصلين فرعيين: عازف البيانو وعازف الكمان . بفضل تعدد الأشكال، سينفذ كل منها نسخته الخاصة من طريقة play() . عند التجاوز، يمكنك استخدام التعليق التوضيحي @Override ، لكنه ليس ضروريًا.
public class Pianist extends Musician {

    String favoritePianoType;

    public Pianist(String name, int age, String favoritePianoType) {
        super(name, age);
        this.favoritePianoType = favoritePianoType;
    }


    @Override
void play(){
        System.out.println("I am playing the piano...");
    }
}
يمكن أن يكون عازف الكمان عازفًا منفردًا أو عضوًا في الأوركسترا. لنأخذ ذلك بعين الاعتبار عند تجاوز طريقة اللعب () الخاصة بنا .
public class Violinist extends Musician {
    boolean isSoloist;

public Violinist(String name, int age, boolean isSoloist) {
            super(name, age);
            this.isSoloist = isSoloist;
        }


    @Override
void play(){
if (isSoloist)
        System.out.println("I am playing the violin solo...");
else
System.out.println("I am playing the violin in an orchestra...");

    }
}
لنقم بإنشاء فئة تجريبية ، حيث سنقوم بإنشاء ثلاثة كائنات، مثيل واحد لكل فئة من الفئات التي تم إنشاؤها مسبقًا. سنرى ما هي النتائج التي سنحصل عليها.
public class Demo {
  public static void main(String[] args) {
  Musician musician = new Musician();
  Violinist violinist = new Violinist("John", 32, true);
  Pianist pianist = new Pianist("Glen", 30, "Acoustic");

  System.out.println("Musician said:");
  musician.play();
  System.out.println("Violinist said:");
  violinist.play();
  System.out.println("Pianist said:");
  pianist.play();
    }
}
وهنا ما حصلنا عليه:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo…
Pianist said:
I am playing the piano...
كل عازف كمان وعازف بيانو هو موسيقي، ولكن ليس كل موسيقي هو عازف كمان أو بيانو. وهذا يعني أنه يمكنك استخدام طريقة تشغيل الموسيقي إذا لم تكن بحاجة إلى إنشاء طريقة جديدة. أو يمكنك استدعاء طريقة الوالدين من الطفل باستخدام الكلمة الأساسية super . دعونا نفعل ذلك في كود عازف البيانو:
public class Pianist extends Musician {

    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
}
الآن دعنا نستدعي طريقتنا الرئيسية () في الفئة التجريبية . وهذه هي النتيجة:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...

طريقة التحميل الزائد

التحميل الزائد للطريقة يعني استخدام طرق مختلفة بنفس الاسم في نفس الفئة. يجب أن تكون مختلفة من حيث عدد أو ترتيب أو أنواع معلماتها. لنفترض أن عازف البيانو يمكنه العزف على البيانو الصوتي والبيانو الكهربائي. لكي يعزف الموسيقي يحتاج إلى كهرباء. لنقم بإنشاء طريقتين مختلفتين للعب () . الأول بدون معلمات للبيانو الصوتي، والثاني بمعلمات تشير إلى توفر الكهرباء.
public class Pianist extends Musician {

    String name;
    int age;
    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
    void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            System.out.println("I am playing the piano...");
        }
        else System.out.println("I can't play this without electricity.");
    }
}
بالمناسبة، يمكنك استخدام طريقة اللعب () الأولى داخل طريقة اللعب (المنطقية) الثانية بهذه الطريقة:
void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            play();
        }
        else System.out.println("I can't play this without electricity.");
    }
دعونا نضيف بعض السطور إلى صفنا التجريبي لتوضيح التحميل الزائد لدينا:
public class Demo {
    public static void main(String[] args) {

        Musician musician = new Musician();
        Violinist violinist = new Violinist("John", 23, true);
        Pianist pianist = new Pianist("Glen", 30, "Acoustic");

        System.out.println("Musician said:");
        musician.play();
        System.out.println("Violinist said:");
        violinist.play();
        System.out.println("Pianist said:");
        pianist.play();
        System.out.println("The pianist will now try the electric piano:");
        pianist.play(true);
        System.out.println("The electricity has been shut off. Now when trying the electric piano, the pianist says:");
        pianist.play(false);
    }
}
وهنا النتيجة:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...
The pianist will now try the electric piano:
The electricity is on.
I am playing my instrument...
I am playing the piano...
The electricity has been shut off. Now when trying the electric piano, the pianist says:
I can't play this without electricity.
تعرف Java الطريقة التي يجب استخدامها بناءً على معلماتها ونوع الكائن. هذا هو تعدد الأشكال.

التجريد

عندما نحدد فئة ما، فإننا نحاول بناء نموذج لشيء ما. على سبيل المثال، لنفترض أننا نكتب لعبة فيديو تسمى MyRacer بسيارات سباق مختلفة. يمكن للاعب اختيار واحدة منها ثم تحديثها لاحقًا أو شراء واحدة مختلفة. إذًا... ما هي السيارة؟ السيارة شيء معقد للغاية، ولكن إذا كنا نحاول إنشاء لعبة فيديو سباق (بدلاً من محاكاة القيادة)، فلن نحتاج إلى وصف جميع آلاف التروس والجوانات التي تحتوي عليها. نحن بحاجة إلى طرازها وسرعتها القصوى وخصائصها والقدرة على المناورة والسعر واللون... وربما يكون هذا كافيًا. هذا هو نموذج السيارة للعبتنا. لاحقًا في MyRacer 2، لنفترض أننا قررنا إضافة الإطارات التي تؤثر على التعامل مع الطريق. هنا النموذج مختلف لأننا أضفنا المزيد من التفاصيل. دعونا نعرّف تجريد البيانات على أنه عملية تحديد الخصائص المهمة (أو الضرورية) للكائن فقط وتجاهل أي تفاصيل غير ذات صلة. هناك مستويات مختلفة من التجريد. على سبيل المثال، إذا كنت راكبًا على متن حافلة، فأنت بحاجة إلى معرفة شكل الحافلة الخاصة بك وإلى أين تتجه، ولكنك لا تحتاج إلى معرفة كيفية قيادتها. إذا كنت سائق حافلة، فلن تحتاج إلى معرفة كيفية إنشاء حافلة جديدة - ما عليك سوى معرفة كيفية قيادتها. ولكن إذا كنت شركة مصنعة للحافلات، فأنت بحاجة إلى الذهاب إلى مستوى أقل من التجريد، لأن تفاصيل تصميم الحافلة مهمة جدًا بالنسبة لك. آمل أن تفهم ما أعنيه.

كيف يعمل في جافا:

دعونا نبني أربعة مستويات من التجريد في Java، أو بالأحرى في OOP - من الأدنى (الأكثر تحديدًا) إلى الأعلى (الأكثر تجريدًا).
  1. أدنى مستوى من التجريد هو كائن محدد. هو كيان له مجموعة من الخصائص التي تنتمي إلى فئة معينة. لديها قيم حقل محددة

  2. قالب إنشاء الكائنات هو فئة. إنه وصف لمجموعة من الكائنات ذات الخصائص والبنية الداخلية المتشابهة.

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

  4. الواجهة عبارة عن إنشاء من بنية لغة برمجة Java التي تحتوي فقط على الأساليب العامة المجردة والحقول الثابتة الثابتة (الثابت النهائي). بمعنى آخر، لا يمكن استخدام الفئات المجردة ولا الواجهات لإنشاء الكائنات.

راجع للشغل، في Java 8 أو الأحدث، يمكن أن تحتوي الواجهات ليس فقط على أساليب وثوابت مجردة، ولكن أيضًا على أساليب افتراضية وثابتة. في Java، تحدد الواجهة السلوك، بينما يتم استخدام فئة مجردة لإنشاء تسلسل هرمي. يمكن تنفيذ واجهة واحدة بواسطة فئات متعددة.

مثال على الواجهة في كود Java

interface Human {
	public void struggle();
	public void protect();
}

interface Vulcan {
	int angleOfPointyEars;
	public void turnOffEmotions(boolean isOn);
	public void telepathy();
}
يمكنك تنفيذ أكثر من واجهة
The Spock class implements Human and Vulcan {
public void struggle() {
System.out.println("I am struggling...");
}
	public void protect() {
System.out.println("You are under my protection!);
}
public void turnOffEmotions(boolean isOn){
If (isOn) {
System.out.println("I am turning off my emotions.");
isOn= !isOn;
}
}
	public void telepathy() {
System.out.println("Connecting to your brain...");
}

}
بالنسبة للطلاب المبتدئين، يغطي هذا المقرر جميع المفاهيم الأساسية للبرمجة الشيئية في Java. إلى جانب مبادئ OOP الأربعة الرئيسية، تحتوي Java أيضًا على الارتباط والتجميع والتكوين. يمكنك تسميتها "مبادئ OOP الإضافية". إنهم يستحقون مقالة منفصلة خاصة بهم.