JavaRush /مدونة جافا /Random-AR /واجهات في جافا

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

نشرت في المجموعة
مرحبًا! سنتحدث اليوم عن مفهوم مهم في Java وهو الواجهات. ربما تكون الكلمة مألوفة لك. على سبيل المثال، تحتوي معظم برامج وألعاب الكمبيوتر على واجهات. بالمعنى الواسع، الواجهة هي نوع من "جهاز التحكم عن بعد" الذي يربط طرفين يتفاعلان مع بعضهما البعض. مثال بسيط لواجهة من الحياة اليومية هو جهاز التحكم عن بعد الخاص بالتلفزيون. فهو يربط بين كائنين، شخص وجهاز تلفزيون، ويقوم بمهام مختلفة: رفع مستوى الصوت أو خفضه، وتغيير القنوات، وتشغيل التلفزيون أو إيقاف تشغيله. يحتاج أحد الجانبين (الشخص) إلى الوصول إلى الواجهة (اضغط على زر التحكم عن بعد) حتى يتمكن الجانب الآخر من تنفيذ الإجراء. على سبيل المثال، لكي يقوم التلفزيون بتحويل القناة إلى القناة التالية. وفي هذه الحالة لا يحتاج المستخدم إلى معرفة جهاز التلفزيون وكيفية تنفيذ عملية تغيير القناة بداخله. لماذا هناك حاجة للواجهات في Java - 1كل ما يمكن للمستخدم الوصول إليه هو الواجهة . المهمة الرئيسية هي الحصول على النتيجة المرجوة. ما علاقة هذا بالبرمجة وجافا؟ مباشر :) إنشاء واجهة يشبه إلى حد كبير إنشاء فئة عادية، ولكن بدلا من الكلمة classنحدد الكلمة interface. دعونا نلقي نظرة على أبسط واجهة Java ونكتشف كيف تعمل وما هو المطلوب من أجله:
public interface Swimmable  {

     public void swim();
}
لقد أنشأنا واجهة Swimmableيمكنها السباحة . هذا يشبه جهاز التحكم عن بعد الخاص بنا، والذي يحتوي على "زر" واحد: الطريقة swim() هي "السباحة". كيف يمكننا استخدام هذا " جهاز التحكم عن بعد "؟ ولهذا الغرض الطريقة، أي. يجب تنفيذ الزر الموجود على جهاز التحكم عن بعد لدينا. لاستخدام واجهة ما، يجب تنفيذ أساليبها بواسطة بعض فئات برنامجنا. دعونا نتوصل إلى فصل دراسي تتناسب أغراضه مع الوصف "يستطيع السباحة". على سبيل المثال، فئة البط مناسبة Duck:
public class Duck implements Swimmable {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
ما الذي نراه هنا؟ يرتبط الفصل Duckبواجهة Swimmableباستخدام الكلمة الأساسية implements. إذا كنت تتذكر، فقد استخدمنا آلية مماثلة لربط فئتين في الميراث، فقط كانت هناك كلمة " يمتد ". يمكن ترجمة " public class Duck implements Swimmable" حرفيًا من أجل الوضوح: "الطبقة العامة Duckتنفذ الواجهة Swimmable." هذا يعني أن الفئة المرتبطة بالواجهة يجب أن تنفذ جميع أساليبها. يرجى ملاحظة: في فصلنا، Duckتمامًا كما هو الحال في الواجهة ، Swimmableتوجد طريقة swim()، ويوجد بداخلها نوع من المنطق. هذا هو شرط إلزامي. إذا كتبنا للتو " public class Duck implements Swimmable" ولم ننشئ طريقة swim()في الفصل Duck، فسيعطينا المترجم خطأ: Duck ليس مجردًا ولا يتجاوز الطريقة المجردة swim() في Swimmable لماذا يحدث هذا؟ إذا شرحنا الخطأ باستخدام مثال التلفزيون، يتبين أننا نعطي شخصًا جهاز تحكم عن بعد مزودًا بزر "تغيير القناة" من تلفزيون لا يعرف كيفية تغيير القنوات. عند هذه النقطة، اضغط على الزر بقدر ما تريد، لن يعمل شيء. جهاز التحكم عن بعد نفسه لا يغير القنوات: فهو يعطي فقط إشارة إلى التلفزيون، حيث يتم تنفيذ عملية معقدة لتغيير القناة. وهذا هو الحال مع بطتنا: يجب أن تكون قادرة على السباحة بحيث يمكن الوصول إليها باستخدام الواجهة Swimmable. إذا لم تكن تعرف كيفية القيام بذلك، فلن تقوم الواجهة Swimmableبتوصيل الجانبين - الشخص والبرنامج. لن يتمكن أي شخص من استخدام طريقة swim()لجعل كائن Duckداخل البرنامج يطفو. لقد رأيت الآن بشكل أكثر وضوحًا ما هي الواجهات المخصصة لها. تصف الواجهة السلوك الذي يجب أن تمتلكه الفئات التي تنفذ تلك الواجهة. "السلوك" هو مجموعة من الأساليب. إذا أردنا إنشاء برامج مراسلة متعددة، فإن أسهل طريقة للقيام بذلك هي إنشاء واجهة Messenger. ما الذي يجب على أي رسول أن يفعله؟ بشكل مبسط استقبال وإرسال الرسائل.
public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
والآن يمكننا ببساطة إنشاء فئات المراسلة الخاصة بنا من خلال تنفيذ هذه الواجهة. المترجم نفسه سوف "يجبرنا" على تنفيذها داخل الفصول. برقية:
public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a message to Telegram!");
    }

     public void getMessage() {
         System.out.println("Reading the message in Telegram!");
     }
}
واتساب:
public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
فايبر:
public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a message to Viber!");
    }

     public void getMessage() {
         System.out.println("Reading a message in Viber!");
     }
}
ما هي الفوائد التي يوفرها هذا؟ وأهمها هو الاقتران السائب. تخيل أننا نقوم بتصميم برنامج نقوم من خلاله بجمع بيانات العملاء. يجب أن يحتوي الفصل Clientعلى حقل يشير إلى برنامج المراسلة الذي يستخدمه العميل. بدون واجهات سيبدو الأمر غريبًا:
public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
لقد أنشأنا ثلاثة حقول، ولكن يمكن للعميل بسهولة أن يكون لديه برنامج مراسلة واحد فقط. نحن فقط لا نعرف أي واحد. ولكي لا تبقى دون التواصل مع العميل، عليك "دفع" جميع الخيارات الممكنة إلى الفصل. اتضح أن واحدًا أو اثنين منهم سيكونان موجودين دائمًا null، وليست هناك حاجة إليهما على الإطلاق حتى يعمل البرنامج. بدلاً من ذلك، من الأفضل استخدام واجهتنا:
public class Client {

    private Messenger messenger;
}
وهذا مثال على "الاقتران السائب"! بدلاً من تحديد فئة مراسلة معينة في الفصل Client، نذكر ببساطة أن العميل لديه رسول. أي واحد سيتم تحديده خلال فترة البرنامج. لكن لماذا نحتاج إلى واجهات لهذا؟ ولماذا تمت إضافتهم إلى اللغة أصلاً؟ السؤال جيد وصحيح! ويمكن تحقيق نفس النتيجة باستخدام الميراث العادي، أليس كذلك؟ الطبقة Messengerهي الطبقة الأم، و Viber، Telegramوهم WhatsAppالورثة. في الواقع، من الممكن القيام بذلك. ولكن هناك صيد واحد. كما تعلم، لا يوجد وراثة متعددة في Java. ولكن هناك تطبيقات متعددة للواجهات. يمكن للفصل تنفيذ أي عدد تريده من الواجهات. تخيل أن لدينا فصلًا Smartphoneبه مجال Application- تطبيق مثبت على هاتف ذكي.
public class Smartphone {

    private Application application;
}
التطبيق والرسول متشابهان بالطبع، لكنهما شيئان مختلفان. يمكن أن يكون Messenger على الهاتف المحمول وعلى سطح المكتب، في حين أن التطبيق هو تطبيق على الهاتف المحمول. لذا، إذا استخدمنا الوراثة، فلن نتمكن من إضافة كائن Telegramإلى الفصل Smartphone. بعد كل شيء، لا يمكن للفصل Telegramأن يرث من Applicationومن Messenger! وقد تمكنا بالفعل من وراثتها Messengerوإضافتها إلى الفصل بهذا النموذج Client. لكن يمكن للفصل Telegramتنفيذ كلتا الواجهتين بسهولة! لذلك، في الفصل Clientيمكننا تنفيذ كائن Telegramكـ Messenger، وفي الفصل Smartphoneكـ Application. وإليك كيف يتم ذلك:
public class Telegram implements Application, Messenger {

    //...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
الآن يمكننا استخدام الفصل Telegramكما نشاء. في مكان ما سوف يتصرف في دور Application, في مكان ما في دور Messenger. ربما لاحظت بالفعل أن الأساليب في الواجهات تكون دائمًا "فارغة"، أي أنها لا تحتوي على أي تطبيق. والسبب في ذلك بسيط: فالواجهة تصف السلوك، ولا تنفذه. "يجب أن تكون جميع كائنات الفئات التي تنفذ الواجهة Swimmableقادرة على التعويم": هذا كل ما تخبرنا به الواجهة. كيف ستسبح السمكة أو البطة أو الحصان بالضبط هو سؤال للفصول الدراسية Fishوليس Duckللواجهة Horse. تماما مثل تغيير القناة هي مهمة التلفزيون. جهاز التحكم عن بعد يمنحك ببساطة زرًا للقيام بذلك. ومع ذلك، لدى Java8 إضافة مثيرة للاهتمام - الأساليب الافتراضية. على سبيل المثال، تحتوي واجهتك على 10 طرق. يتم تنفيذ 9 منها بشكل مختلف في فئات مختلفة، ولكن يتم تنفيذ واحد منها بنفس الطريقة في الكل. في السابق، قبل إصدار Java8، لم يكن للطرق الموجودة داخل الواجهات أي تنفيذ على الإطلاق: ألقى المترجم خطأً على الفور. الآن يمكنك القيام بذلك على النحو التالي:
public interface Swimmable {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
باستخدام الكلمة الأساسية default، قمنا بإنشاء طريقة في الواجهة مع تطبيق افتراضي. سنحتاج إلى تنفيذ الطريقتين الأخريين، eat()وأنفسنا run()في جميع الفئات التي سيتم تنفيذها Swimmable. ليست هناك حاجة للقيام بذلك باستخدام الطريقة swim(): سيكون التنفيذ هو نفسه في جميع الفئات. بالمناسبة، لقد صادفت واجهات أكثر من مرة في المهام السابقة، على الرغم من أنك لم تلاحظ ذلك بنفسك :) إليك مثال واضح: لقد لماذا نحتاج إلى واجهات في Java - 2عملت مع واجهات Listو Set! بتعبير أدق، مع تطبيقاتها - ArrayListو LinkedList، HashSetوغيرها. يُظهر الرسم البياني نفسه مثالاً عندما يقوم فصل واحد بتنفيذ عدة واجهات في وقت واحد. على سبيل المثال، LinkedListيقوم بتنفيذ الواجهات Listو Deque(قائمة الانتظار ذات الوجهين). أنت أيضًا على دراية بالواجهة Map، أو بالأحرى، بتطبيقاتها - HashMap. بالمناسبة، في هذا المخطط يمكنك رؤية ميزة واحدة: يمكن وراثة الواجهات من بعضها البعض. الواجهة SortedMapموروثة من قائمة الانتظار Mapوموروثة Dequeمن قائمة الانتظار Queue. يعد ذلك ضروريًا إذا كنت تريد إظهار الاتصال بين الواجهات، ولكن إحدى الواجهات هي نسخة موسعة من أخرى. لنلقِ نظرة على مثال للواجهة Queue- قائمة الانتظار. لم نقم بمراجعة المجموعات بعد Queue، لكنها بسيطة للغاية ومرتبة مثل الخط العادي في المتجر. يمكنك إضافة عناصر فقط إلى نهاية قائمة الانتظار، وإزالتها من البداية فقط. في مرحلة معينة، كان المطورون بحاجة إلى نسخة موسعة من قائمة الانتظار بحيث يمكن إضافة العناصر واستلامها من كلا الجانبين. هذه هي الطريقة التي تم بها إنشاء الواجهة Deque- قائمة انتظار ثنائية الاتجاه. يحتوي على كافة أساليب قائمة الانتظار العادية، لأنه "الأصل" لقائمة انتظار ذات اتجاهين، ولكن تم إضافة أساليب جديدة.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION