مشروع: هرمية الأشكال الهندسية
مشروع: هرمية الأشكال الهندسية
يجمع هذا الدرس الختامي كل ما تعلّمته في هذا الفصل — الفئات المجردة، والوراثة، وتجاوز التوابع، والتعددية، والإرسال الديناميكي — في برنامج صغير ولكنّه مكتمل. ستصمّم هرمية للشكل Shape، وتُنفّذ صنفَين فرعيَّين محدَّدَين، وستكتب أداة تعمل مع أي شكل من خلال قائمة متعددة الأنواع.
الهدف
بناء برنامج يستطيع:
- تمثيل أشكال هندسية مختلفة (دوائر، مستطيلات — قابل للتوسعة لأشكال أخرى).
- حساب مساحة كل شكل دون أن يحتاج المستدعي إلى معرفة نوعه.
- حساب مجموع المساحات لمجموعة مختلطة من الأشكال.
هذا عرض كلاسيكي لسبب وجود الوراثة والتعددية: تكتب خوارزمية واحدة (totalArea) تعمل مع أشكال لم تخترعها بعد.
الخطوة الأولى — الفئة الأساسية المجردة
تُعرِّف الفئة المجردة العقد الذي يجب أن تلتزم به جميع الأشكال دون تقييد أي تنفيذ محدَّد. لا معنى لتنفيذ area() على مستوى Shape — إذ سيحسبها كل صنف فرعي بطريقة مختلفة — لذا نُعلنها abstract.
getClass().getSimpleName()؟ لأن describe() تعيش في الفئة الأساسية المجردة لكنّها تُستدعى على كائن من صنف فرعي، فتُعيد getClass() النوع الفعلي وقت التشغيل (Circle أو Rectangle أو غيرهما). هذا هو الإرسال الديناميكي يعمل داخل الفئة الأساسية نفسها.
الخطوة الثانية — الصنف الفرعي Circle
Circle غير صالح على الإطلاق. هذا يُبقي كائناتك دائمًا في حالة متّسقة وذات معنى.
الخطوة الثالثة — الصنف الفرعي Rectangle
الخطوة الرابعة — حساب المجموع بالتعددية
هنا تظهر الفائدة الحقيقية. يقبل تابع totalArea قائمة List<Shape> — لا يعرف ولا يهتم بما إذا كانت القائمة تحتوي على دوائر أو مستطيلات أو أشكال لم تُصنَع بعد. كل استدعاء لـ s.area() يُرسَل إلى التنفيذ الصحيح وقت التشغيل.
الخطوة الخامسة — تجميع كل شيء
مثال على الإخراج:
لماذا يعمل هذا التصميم؟
- مبدأ الفتح/الإغلاق: إضافة صنف
Triangleلا تستلزم أي تغيير فيShapeCalculatorأوMain(سوى إضافته للقائمة). الكود الموجود مغلق أمام التعديل ومفتوح أمام التوسعة. - المسؤولية الفردية: تعرف
Circleكيف تحسب مساحة الدائرة. تعرفShapeCalculatorكيف تجمع المساحات. لا تداخل بينهما. - التعددية تُلغي سلاسل
if/instanceof: بدون البرمجة كائنية التوجه ستكتبif (s instanceof Circle) ... else if (s instanceof Rectangle) ...— كتلة هشّة يجب تحديثها مع كل شكل جديد.
if (shape instanceof Circle) { ... } else if (shape instanceof Rectangle) { ... } يُفسد الغرض من التعددية. إذا وجدت نفسك تكتب هذا النمط، انقل السلوك إلى تابع مُجاوَز في كل صنف فرعي عوضًا عن ذلك.
توسعة الهرمية
لإضافة مثلث Triangle تحتاج فقط إلى كتابة صنف واحد جديد — لا يتغيّر شيء في بقية البرنامج:
أضف new Triangle(6, 4) إلى القائمة في Main وسيعالجه totalArea دون تغيير سطر واحد آخر.
الخلاصة
لقد بنيت هرمية صغيرة لكنّها ذات شكل إنتاجي حقيقي: فئة أساسية مجردة تُعرِّف العقد، أصناف فرعية محددة تُنفِّذه، وخوارزمية متعددة الأشكال تعمل على أي شكل من خلال النوع الأساسي. هذا النمط — نوع مجرد، تنفيذات متعددة، خوارزمية واحدة — هو أساس عدد لا يُحصى من واجهات Java البرمجية الحقيقية بما فيها java.io.InputStream وjava.util.Collection وكل مكتبة مكونات واجهة مستخدم كُتبت على الإطلاق.