لماذا لا توجد وراثة متعددة للكلاسات
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 26 من 87.
تمنع لغة Java بشكل متعمد أي فئة (class) من وراثة أكثر من فئة واحدة. يوجد هذا القيد بسبب مشكلة تُعرف باسم مشكلة الماس (Diamond Problem).
تخيل لو كانت لغة Java تسمح بالوراثة المتعددة. فكر في هذا السيناريو الافتراضي:
// هذا الكود غير صالح في Java
public class A {
public void greet() {
System.out.println("Hello from A");
}
}
public class B extends A {
@Override
public void greet() {
System.out.println("Hello from B");
}
}
public class C extends A {
@Override
public void greet() {
System.out.println("Hello from C");
}
}
// غير مسموح به في Java!
public class D extends B, C { }إذا قمت باستدعاء greet() على كائن D، فأي نسخة يجب أن تعمل؟ تلك الخاصة بـ B أم C؟ هذا الغموض هو "مشكلة الماس" (Diamond Problem)، والتي سميت بهذا الاسم نسبة إلى شكل الماسة الناتج عن مخطط الوراثة.
حل جافا بسيط: السماح بالوراثة الفردية فقط للفئات. ومع ذلك، توفر جافا بديلاً. يمكن للفئة تنفيذ واجهات متعددة، والتي ستتعلم عنها في فصل قادم.
تعالج الواجهات مشكلة الماس (diamond problem) بشكل مختلف وتمنحك المرونة دون غموض.
// هذا صالح في Java
public class Dog extends Animal implements Runnable, Comparable<Dog> {
// يمكن تنفيذ واجهات متعددة
}هذا الخيار التصميمي يحافظ على نموذج الوراثة في Java نظيفاً وقابلاً للتوقع، مع السماح في الوقت نفسه بإعادة استخدام قوية للكود من خلال الواجهات (interfaces).
التحدي
سهللنقم ببناء نظام مشغل موسيقى يوضح قاعدة الوراثة الأحادية في Java وكيف لا يزال بإمكانك تحقيق تصميمات مرنة ضمن هذا القيد.
تخيل أنك تريد إنشاء مكبر صوت ذكي يجمع بين ميزات كل من مشغل الموسيقى والمساعد الصوتي. نظرًا لأن Java لا تسمح بتوسيع فئات متعددة، فستحتاج إلى تصميم التسلسل الهرمي الخاص بك بعناية باستخدام الوراثة الأحادية.
ستقوم بإنشاء أربعة ملفات لتنظيم الكود الخاص بك:
AudioDevice.java: قم بإنشاء الفئة الأساسية التي تشترك فيها جميع الأجهزة التي تشغل الصوت. يجب أن تحتوي على:- حقل خاص لـ
deviceName(String) - منشئ (constructor) يقبل اسم الجهاز
- دالة
getDeviceName()تعيد الاسم - دالة
playSound()تطبع:[deviceName]: Playing audio
- حقل خاص لـ
MusicPlayer.java: قم بإنشاء فئة ترث من AudioDevice وتضيف ميزات خاصة بالموسيقى:- حقل خاص لـ
currentSong(String) - منشئ يأخذ اسم الجهاز ويستخدم
super(deviceName) - دالة
setSong(String song)لتعيين الأغنية الحالية - تجاوز (Override) لدالة
playSound()لتطبع:[deviceName]: Now playing - [currentSong]
- حقل خاص لـ
SmartSpeaker.java: قم بإنشاء فئة ترث من MusicPlayer (وليس من كل من MusicPlayer وفئة VoiceAssistant أخرى - فهذا سيكون وراثة متعددة!). يعتمد مكبر الصوت الذكي على مشغل الموسيقى من خلال إضافة ميزات صوتية:- حقل خاص لـ
assistantName(String) - منشئ يأخذ اسم الجهاز واسم المساعد، باستخدام
super(deviceName) - دالة
voiceCommand(String command)تطبع:[assistantName]: Processing "[command]" - دالة
getFullInfo()تطبع:[deviceName] with [assistantName] assistant
- حقل خاص لـ
Main.java: استعرض سلسلة الوراثة متعددة المستويات الخاصة بك. ستتلقى ثلاثة مدخلات: اسم الجهاز (String)، واسم المساعد (String)، واسم الأغنية (String). قم بإنشاء SmartSpeaker، وعيّن أغنية، ثم استدعِ هذه الدوال بالترتيب:getFullInfo()playSound()voiceCommand("skip to next")
ستتلقى ثلاثة مدخلات: اسم الجهاز، واسم المساعد، واسم الأغنية.
لاحظ كيف يكتسب SmartSpeaker قدرات الموسيقى من خلال وراثة MusicPlayer في سلسلة (AudioDevice -> MusicPlayer -> SmartSpeaker)، بدلاً من محاولة وراثة فئات متعددة غير مرتبطة. هذا هو نهج Java للحفاظ على الوراثة نظيفة وغير غامضة!
ورقة مرجعية
تسمح لغة Java فقط بـ الوراثة الفردية (single inheritance) - حيث يمكن للفئة (class) أن ترث من فئة واحدة أخرى فقط. هذا يمنع مشكلة الألماسة (Diamond Problem)، حيث ينشأ الغموض إذا كان بإمكان الفئة وراثة نفس الطريقة (method) من فئات أب متعددة.
// غير مسموح به في Java - الوراثة المتعددة
public class D extends B, C { } // خطأ!ومع ذلك، تسمح Java للفئة بتنفيذ عدة واجهات (interfaces) كبديل:
// صالح في Java
public class Dog extends Animal implements Runnable, Comparable<Dog> {
// يمكن توسيع فئة واحدة وتنفيذ واجهات متعددة
}تدعم Java الوراثة متعددة المستويات (multilevel inheritance) - إنشاء سلاسل وراثة:
public class AudioDevice { }
public class MusicPlayer extends AudioDevice { }
public class SmartSpeaker extends MusicPlayer { }
// يرث SmartSpeaker من كل من MusicPlayer و AudioDeviceجرّب بنفسك
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// قراءة المدخلات
String deviceName = scanner.nextLine();
String assistantName = scanner.nextLine();
String songName = scanner.nextLine();
// TODO: قم بإنشاء SmartSpeaker باستخدام deviceName و assistantName
// TODO: قم بتعيين الأغنية باستخدام setSong()
// TODO: قم باستدعاء getFullInfo()
// TODO: قم باستدعاء playSound()
// TODO: قم باستدعاء voiceCommand("skip to next")
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةمقدمة في الـ OOPالفئات (Classes) مقابل الكائنات (Objects)الكلمة المفتاحية thisالدوال (Methods)الحقول (Attributes)دالة البناء (Constructor)تعدد أشكال دالة البناء (Constructor Overloading)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسية (extends)الكلمة المفتاحية superإعادة تعريف الدوال (@Override)تسلسل المنشئات (Constructor Chaining)كلاس Objectالوراثة الأحادية ومتعددة المستوياتلماذا لا توجد وراثة متعددة للكلاساتمراجعة - هيكلية الموظفين7الدوال الخاصة وفئة Object
دالة toString()equals() و hashCode()دالة clone()compareTo() و Comparableواجهة Comparatorمراجعة - الترتيب المخصص2محددات الوصول والتغليف
نظرة عامة على مستويات الوصولدوال Getter و Setterإخفاء البياناتالكلمة المفتاحية finalمراجعة - مدير الحساب البنكي5تعدد الأشكال (Polymorphism)
أساسيات الـ Method Overloadingالـ Method Overriding (في وقت التشغيل)الـ Upcasting والـ Downcastingمعامل الـ instanceofالفئات والدوال المجردة (Abstract)مراجعة - حاسبة الأشكال8مفاهيم OOP المتقدمة
الـ Composition مقابل الـ Inheritanceالـ Aggregation مقابل الـ Compositionالفئات الداخلية والمتداخلة والمجهولة (Inner, Nested & Anonymous)الـ Enums ودوالهاالـ Records (Java 16+)الـ Sealed Classes (Java 17+)11أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Builderنمط Observerنمط Strategy3خصائص الفئة والعضو الساكن
المتغيرات الخاصة بالكائن مقابل المتغيرات الساكنةالدوال الساكنةالكتل البرمجية الساكنةالثوابت (static final)ملخص - العداد والأدوات المساعدة6الواجهات (Interfaces) والأصناف المجردة (Abstract Classes)
مقدمة في الواجهات (Interfaces)تنفيذ الواجهات (Interfaces)تنفيذ واجهات متعددةالـ Default و Static في الواجهاتالأصناف المجردة مقابل الواجهاتالواجهات الوظيفية (Functional Interfaces)مراجعة - نظام الدفع9الأنواع العامة (Generics)
مقدمة في الأنواع العامةالأصناف العامة (Generic Classes)الدوال العامة (Generic Methods)معاملات الأنواع المقيدةالرموز البديلة (?, extends, super)مراجعة - الحاويات العامة12أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر