GagorAcadmey

تعلم برمجة تطبيقات الاندرويد من الصفر حتي الاحتراف

آخر المواضيع

الثلاثاء، 4 سبتمبر 2018

الواجهات في جافا

مفهوم الإنترفيس

interface هي كلمة محجوزة في جافا, و تكتب إنترفيس في اللغة العربية.
في دروس سابقة رأينا أنه في جافا لا يمكن للكلاس العادي أن يرث من أكثر من كلاس في نفس الوقت لأنه يمكن للكلاس أن يفعل extends لكلاس واحد فقط.
من أجل ذلك عمل مطوروا لغة جافا على إبتكار نوع جديد يشبه الكلاس العادي و يسمح لنا بتطبيق مبدأ تعدد الوراثة الذي يعتبر شيء مهم جداً في لغات البرمجة. هذا النوع الجديد يسمى interface.
يستخدم الإنترفيس كوسيط لتطوير البرامج, و المقصود هنا أن المبرمج يستخدم الإنترفيس إذا أراد جعل برنامجه قابلاً للتطوير بكل مرونة سواءً لأجل نفسه أو من قبل مبرمجين آخرين, فهو يستخدم الإنترفيس لإجبار المبرمجين على اتباع أسلوب محدد يضمن توافق الكودات التي سيكتبها المطورون الآخرون مع كودات البرنامج الأساسية.
إذاً الإنترفيس يستخدم لتجهيز أسلوب يمكن السير عليه لربط الكلاسات بأسلوب سهل, منطقي و مفهوم. سترى قوة الإنترفيس الحقيقية في درس الـ Polymorphism لكننا سنركز في هذا الدرس على طريقة التعامل مع الإنترفيس في جافا.

التعامل مع الإنترفيس

التعامل مع الإنترفيس يختلف عن التعامل مع الكلاس العادي, و هو يشبه التعامل مع الـ Abstract Class.
فعلياً الإنترفيس يطبق مبدأ الـ Full Abstraction.

الأشياء التي يمكن تعريفها بداخل الإنترفيس

  1. دوال لا تملك body, أي Abstract Method.

  2. متغيرات مع إعطائهم قيمة بشكل مباشرةً عند تعريفهم. لأن أي متغير تقوم بتعرفه بداخل الإنترفيس يعتبر معرف كـ public final static بشكل تلقائي.

  3. Nested Classes, أي كلاس نوعه static بداخل كلاس نوعه static.

  4. Nested Interfaces, أي إنترفيس بداخل إنترفيس.


طريقة تعريف الإنترفيس

الإنترفيس هو في الأساس Full Abstract, لكن لا يجب وضع الكلمة abstract عند تعريفه. و لا يمكن تعريف إنترفيس كـ prviate أو protected لأنه دائماً يعتبر public حتى لو لم تضع كلمة public قبله. كما أنه لا يمكن تعريف الإنترفيس كـ final أو static لأنه تم تصميم الإنترفيس لجعل أي كلاس يرثه يفعل Override للدوال الموجودة فيه.
إذاً لتعريف إنترفيس, أكتب interface ثم ضع له أي إسم تريده.
interface MyInterface {

}

طريقة تعريف متغيرات في الإنترفيس

متغيرات الإنترفيس تعتبر public final static حتى لو لم تقم بتعريفها كذلك, الأمر الذي يجعلك مجبراً على إعطاءها قيمة مباشرةً عند تعريفها مع عدم إمكانية تغيير هذه القيمة.
interface MyInterface {
int X = 1;// حتى و لم نعرفه كذلك public final static يعتبر معرف كـ X المتغير
}


طريقة تعريف دوال في الإنترفيس

دوال الإنترفيس هي في الأساس Abstract Method, إذاً لا حاجة إلى وضع الكلمة abstract عند تعريف أي دالة بداخله. كما أن أي دالة يتم تعريفها بداخله تعتبر public حتى لو لم تعرفها كـ public. و لا يمكن تعريف الدوال فيه كـ prviate أو protected أو final أو static.
interface MyInterface {

void myMethod(); // حتى و لم نعرفها كذلك public abstract تعتبر معرفة كـ myMethod() الدالة
long myResult(); // حتى و لم نعرفها كذلك public abstract تعتبر معرفة كـ myResult() الدالة

}

شروط الربط بين الكلاس و الإنترفيس

  1. لا يمكن إنشاء كائن من إنترفيس.

  2. يستطيع الكلاس أن يرث من كلاس واحد, أي يستطيع أن يفعل extends لكلاس واحد.

  3. لا يستطيع الكلاس أن يرث من إنترفيس, أي لا يستطيع أن يفعل extends لإنترفيس.

  4. يستطيع الكلاس تنفيذ إنترفيس أو أكثر, أي يستطيع أن يفعل implements لإنترفيس أو أكثر.

  5. الكلاس الذي ينفذ إنترفيس, يجب أن يفعل Override لجميع الدوال التي ورثها من هذا الإنترفيس .

  6. يستطيع الإنترفيس أن يرث من إنترفيس أو أكثر. أي يستطيع الإنترفيس أن يفعل extends لإنترفيس أو أكثر.



تنفيذ الإنترفيس

عند تنفيذ أي إنترفيس, يجب أن تفعل Override لجميع الدوال الموجودة فيه, و يجب تعريفهم كـ public حتى يستطيع أي كائن من هذا الكلاس أن يستخدمهم.
A.java
interface A { // لجميع الدوال الموجودة فيه Override أي كلاس ينفذ هذا الإنترفيس يجب أن يفعل
void print(); }
B.java
class B implements A { // A ينفذ الإنترفيس B هنا قلنا أن الكلاس
  @Override // public لا تنسى إضافة كلمة .print() للدالة Override مجبور أن يفعل B الكلاس
public void print() {
System.out.println("B should Override this method");
    }
}

شروط أساسية عليك اتباعها عند إنشاء إنترفيس

  1. لا تستخدم أي Access Modifer عند تعريف إنترفيس.

  2. لا تستخدم أي Access Modifer عند تعريف دالة بداخل إنترفيس.

  3. بداخل الإنترفيس جميع الدوال يجب أن لا تملك body. و يمكن جعل الدالة ترمي إستثناء.

  4. لا يمكن للإنترفيس أن يملك كونستركتور.

الأسباب التي تدفعك إلى استخدام الإنترفيس

  1. إذا كنت تنوي بناء كلاس يطبق مفهوم الـ Full Abstraction.

  2. لحل مشكلة تعدد الوراثة.

  3. لتطبيق مبدأ تعدد الأشكال Polymorphism.

الفرق بين الكلمتين extends و implements

  1. extends تعني وراثة, نستخدمها لجعل كلاس يرث من كلاس, أو لجعل إنترفيس يرث من إنترفيس أو أكثر.

  2. implements تعني تنفيذ, نستخدمها لجعل كلاس ينفذ إنترفيس أو أكثر.


الصورة التالية توضح لك متى يمكن إستخدام الكلمتين extends و implements.

متى تكون مجبراً على أن تفعل Override و متى لا

في حالة الـ extends:
  1. إذا كان عندك كلاس يرث من كلاس, يكون للـ Subclass الحرية في أن يفعل Override للدوال التي ورثها.

  2. إذا كان عندك كلاس يرث من كلاس نوعه abstract, يكون الـ Subclass مجبراً على أن يفعل Override للدوال المعرفة كـ abstract.

  3. إذا كان عندك إنترفيس يرث من إنترفيس, لا يستطيع الـ SubInterface أن يفعل Override للدوال التي ورثها.

في حالة الـ implements:
  1. إذا كان عندك كلاس ينفذ إنترفيس, يكون الـ Subclass مجبراً على أن يفعل Override للدوال التي ورثها.

  2. إذا كان عندك كلاس نوعه abstract ينفذ إنترفيس, يكون الـ Subclass له الحرية في أن يفعل Override للدوال التي ورثها.

أشكال تعدد الوراثة في جافا

إذا قام كلاس بتنفيذ أكثر من إنترفيس, أو إذا قام إنترفيس بوراثة أكثر من إنترفيس, تسمى هذه العمليات وراثة متعددة (Multiple Inheritance).
الصورة التالية توضح لك شكل الوراثة المتعددة.

الوراثة من أكثر من كلاس في جافا

ملاحظة

إذا كان الكلاس ينفذ أكثر من إنترفيس, يجب وضع فاصلة بينهم.
في حال كان الكلاس يرث من كلاس آخر و ينفذ إنترفيس أو أكثر, إفعل extends للكلاس في البداية ثم إفعل implements لأي إنترفيس تريد.

المثال الأول

هنا قمنا بتعريف إثنين إنترفيس A و B, و قمنا بتعريف كلاس إسمه C و قلنا أن C ينفذ A و B.
interface A { }

interface B { }

class C implements A, B { } // لكل دالة ورثها منهما Override بمعنى أنه يجب أن يفعل B و الإنترفيس A ينفذ الإنترفيس C هنا الكلاس


المثال الثاني

هنا قمنا بتعريف ثلاثة إنترفيس A ,B و C, و قلنا أن C يرث من A و B.
interface A { }

interface B { }

interface C extends A, B { } // B و الإنترفيس A يرث من الإنترفيس C هنا الإنترفيس


المثال الثالث

هنا قمنا بتعريف إنترفيس A, كلاس B و كلاس C يرث من B و ينفذ A.
interface A { }

class B { }

class C extends B implements A { } // A و ينفذ الإنترفيس B يرث من الكلاس C هنا الكلاس

أمثلة توضيحية علي ما سبق

المثال الأول

في هذا المثال قمنا بتعريف إثنين إنترفيس A و B:

  1. الإنترفيس A يملك دالة إسمها printA().

  2. الإنترفيس B يملك دالة إسمها printB().

ثم قمنا بتعريف كلاس إسمه C و قلنا أن C ينفذ A و B.


A.java
public interface A {
 
    void printA();
 
}

B.java
public interface B {
 
    void printB();
 
}

C.java
public class C implements A, B {// لجميع الدوال التي ورثها Override إذاً يجب أن يفعل B و الإنترفيس A ينفذ الإنترفيس C هنا الكلاس
 
    @Override// printA() للدالة Override مجبور أن يفعل C الكلاس
    void printA() {
        System.out.println("C should Override the method printA()");
    }
 
    @Override// printB() للدالة Override مجبور أن يفعل C الكلاس
    void printB() {
        System.out.println("C should Override the method printB()");
    }
 
}

المثال الثاني

في هذا المثال قمنا بتعريف إنترفيس A يملك دالة إسمها printA().
و إنترفيس B يرث من الإنترفيس A و يملك دالة إسمها printB().

ثم قمنا بتعريف كلاس إسمه C يطبق الإنترفيس B, و بالتالي عليه أن يفعل Override لجميع الدوال التي ورثها.

A.java
public interface A {
 
    void printA();
 
}

B.java
public interface B extends A {
 
    void printB();
 
}

C.java
public class C implements B {// printB() و printA() للدالتين Override إذاً يجب أن يفعل .B و الذي بدوره يرث من الإنترفيس B يطبق من الإنترفيس C هنا الكلاس
 
    @Override// printA() للدالة Override مجبور أن يفعل C الكلاس
    void printA() {
        System.out.println("C should Override the method printA()");
    }
 
    @Override// printB() للدالة Override مجبور أن يفعل C الكلاس
    void printB() {
        System.out.println("C should Override the method printB()");
    }
 
}

المثال الثالث

في هذا المثال قمنا بتعريف إنترفيس A, كلاس B و كلاس C يرث من B و ينفذ A:

  1. الإنترفيس A يملك دالة إسمها printA().

  2. الإنترفيس B يملك دالة إسمها printB().


A.java
public interface A {
 
    void printA();
 
}

B.java
public public class B {
 
    void printB() {
        System.out.println("C not need to Override the method printB()");
    }
 
}

C.java
public class C extends B implements A {// A و ينفذ الإنترفيس B يرث من الكلاس C هنا الكلاس
 
    @Override// printA() للدالة Override مجبور أن يفعل C الكلاس
    void printA() {
        System.out.println("C should Override the method printA()");
    }
 
}

Nested Interfaces

يمكنك تعريف إنترفيس بداخل إنترفيس بداخل إنترفيس إلخ.. ويمكنك تنفيذ الإنترفيس الذي تريده منهم بالتحديد متى شئت.

المثال الأول

هنا قمنا بتعريف إنترفيس A, و بداخله إنترفيس B, و بداخله إنترفيس C
ثم قمنا بتعريف كلاس D ينفذ A, و كلاس E ينفذ B, و كلاس F ينفذ C
interface A { // A نكتب A لنصل للإنترفيس
interface B { // A.B نكتب A الموجود بداخل الإنترفيس B لنصل للإنترفيس
interface C { // A.B.C نكتب A الموجود بداخل الإنترفيس B الموجود بداخل الإنترفيس C لنصل للإنترفيس
     }
   }
}

class D implements A { } // A ينفذ الإنترفيس D هنا الكلاس

class E implements A.B { } // B ينفذ الإنترفيس E هنا الكلاس

class F implements A.B.C { } // C ينفذ الإنترفيس F هنا الكلاس

يمكنك أن تفعل import للإنترفيس و عندها يمكنك أن تكتب إسمه فقط للوصول إليه.

المثال الثاني

هنا سنفعل import للإنترفيس B و import للإنترفيس C بدل أن نصل إليهم من الإنترفيس A.
package interfaces;

import interfaces.A.B; // لذلك أصبح يمكننا الوصول إليه مباشرةً B هنا قمنا بتحديد المكان الموجود فيه الإنترفيس
import interfaces.A.B.C; // لذلك أصبح يمكننا الوصول إليه مباشرةً C هنا قمنا بتحديد المكان الموجود فيه الإنترفيس

interface A { // A نكتب A لنصل للإنترفيس
interface B { // B نكتب A الموجود بداخل الإنترفيس B لنصل للإنترفيس
interface C { // C نكتب A الموجود بداخل الإنترفيس B الموجود بداخل الإنترفيس C لنصل للإنترفيس
     }
}
}

class D implements A { } // A ينفذ الإنترفيس D هنا الكلاس

class E implements B { } // B ينفذ الإنترفيس E هنا الكلاس

class F implements C { } // C ينفذ الإنترفيس F هنا الكلاس

إنتبه:
هنا الإنترفيس B لا يعتبر أنه يرث من الإنترفيس A. كما أن الإنترفيس C لا يعتبر أنه يرث من الإنترفيس B.

Tagged Interfaces

Tagged Interface يقال لها أيضاً Marker Interface, فكرتها تقسيم الـ Interfaces المتشابهين في مجموعات.
لتقسيم الـ Interfaces إلى مجموعات ننشئ إنترفيس فارغ, ثم نجعل كل إنترفيس يشبهه من حيث الفكرة يرث منه.
الآن لنفترض أننا نريد إنشاء مجموعة خاصة للألوان. أول شيء نفعله هو إنشاء إنترفيس فارغ خاص للألوان إسمه Colors.
بعدها نجعل كل إنترفيس له علاقة بالألوان يرث منه, و هكذا سيعتبر المترجم أن كل إنترفيس يرث من Colors هو من النوع Colors.

interface Colors { }
interface RGB implements Colors { } // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس RGB الإنترفيس
interface PMS implements Colors { } // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس PMS الإنترفيس
interface HEX implements Colors { } // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس HEX الإنترفيس
interface HSL implements Colors { } // Colors أي ينتمي إلى المجموعة Colors يرث من الإنترفيس HSL الإنترفيس
في دروس سابقة قلنا أنه في جافا لا يمكن للكلاس أن يرث من أكثر من كلاس في نفس الوقت, إذاً لا يستطيع الكلاس أن يفعل extends لأكثر من كلاس.

مثال

لنفترض أننا قمنا بتعريف ثلاث كلاسات a, B و C. ثم قلنا أن C يرث من A و B.
class A { }

class B { }

class C extends A, B { } // هنا سيظهر لك تحذير و لن يعرف أصلاً المشكلة لأن هذا الأسلوب غير معرف في جافا
إذاً وراثة أكثر من كلاس ممنوعة في جافا.

من هنا قام مطوروا جافا ببناء شيء شبيه للكلاس إسمه interface الهدف الأساسي منه كان دعم فكرة تعدد الوراثة.
الآن إذا عدنا للمثال السابق يمكننا إنجاح عملية تعدد الوراثة باستخدام interface بدل class.

هنا قمنا بتعريف إثنين إنترفيس A و B, و قمنا بتعريف كلاس إسمه C و قلنا أن C يرث من A و B.
interface A { }

interface B { }

class C implements A, B { } // B و الإنترفيس A يرث من الإنترفيس C هنا الكلاس
هنا سيكون الكلاس C مجبر أن يفعل Override لكل دالة موجودة في A و B.

إذاً في جافا يمكنك تنفيذ أكثر من إنترفيس في نفس الوقت.
كلمة implements شبيهة جداً للكلمة extends لكننا وضعناها بدلاً منها لسبب ستفهمه لاحقاً.
بما أن الكلاس يستطيع أن ينفذ أكثر من interface في نفس الوقت, يمكننا القول أن جافا تدعم تعدد الوراثة من خلال الـ interfaces.

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.

صفحتنا علي الفيسبوك

التسميات

المتواجدين حاليا