خدمة تأجير المركبات
جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 87 من 87.
لهذا التحدي النهائي، ستقوم ببناء نظام خدمة تأجير المركبات يجمع بين جميع مفاهيم البرمجة كائنية التوجه (OOP) التي أتقنتها طوال هذه الدورة.
يجب أن يقوم نظامك بنمذجة أسطول من أنواع المركبات المختلفة. فكر في فئة Vehicle أساسية بخصائص مشتركة مثل لوحة الترخيص، والعلامة التجارية، ومعدل الإيجار اليومي. الفئات الفرعية مثل Car، و Motorcycle، و Truck يمكنها توسيع ذلك بخصائص محددة مثل عدد المقاعد، أو حجم المحرك، أو سعة الحمولة.
يمكن للواجهات (Interfaces) تحديد سلوكيات التأجير: قد تعلن واجهة Rentable عن دوال (methods) مثل rent() و returnVehicle() و calculateCost(int days). يمكن لأنواع المركبات المختلفة تنفيذ استراتيجيات تسعير بشكل مختلف، مما يجعل هذا حالة استخدام مثالية لتعدد الأشكال (polymorphism).
فكر في كيفية قيام فئة RentalAgency بإدارة الأسطول باستخدام التركيب (composition).
الاستثناءات المخصصة مثل VehicleNotAvailableException يمكنها التعامل مع الحالات الاستثنائية.
قد تقوم بتطبيق نمط المصنع (Factory pattern) لإنشاء المركبات أو استخدام الـ enums لفئات المركبات وحالة التأجير.
يختبر هذا التحدي قدرتك على تصميم نظام برمجة كائنية التوجه (OOP) كامل وجيد التنظيم. بالتوفيق!
التحدي
سهللنقم ببناء خدمة تأجير مركبات كاملة تجمع كل ما تعلمته عن البرمجة كائنية التوجه (OOP)! ستقوم بإنشاء نظام يدير أسطولاً من أنواع مختلفة من المركبات، ويتعامل مع عمليات التأجير مع التحقق المناسب من الصحة، ويستخدم نمط المصنع لإنشاء المركبات.
ستقوم بتنظيم الكود الخاص بك عبر سبعة ملفات:
RentalStatus.java: أنشئ enum يمثل حالة تأجير المركبة. قم بتضمين القيمAVAILABLE، وRENTED، وMAINTENANCE. أضف حقلاً خاصاًdescription(String)، ومنشئاً (constructor)، وطريقة getter. يجب أن يكون للقيمAVAILABLEالوصف "ready to rent"، وللقيمةRENTEDالوصف "currently in use"، وللقيمةMAINTENANCEالوصف "under service".VehicleNotAvailableException.java: أنشئ استثناءً مخصصاً يرث منException. يجب أن يقبل المنشئ رقم اللوحة (license plate) وينشئ رسالة بالتنسيقVehicle not available: [licensePlate].Rentable.java: أنشئ واجهة (interface) تحدد عمليات التأجير. صرّح عن الطرقrent()التي تعيد boolean، وreturnVehicle()التي تعيد boolean، وcalculateCost(int days)التي تعيد double.Vehicle.java: أنشئ فئة أساسية مجردة (abstract base class) لجميع المركبات. قم بتضمين حقول خاصة لـlicensePlate(String)، وbrand(String)، وdailyRate(double)، وstatus(RentalStatus، مهيأة بـ AVAILABLE). يأخذ المنشئ licensePlate و brand و dailyRate. قم بتضمين طرق getter المناسبة وطريقة setter للحالة. صرّح عن طريقة مجردةgetVehicleType()تعيد String. قم بتنفيذ طريقةgetDetails()التي تعيد"[licensePlate] [brand] ([vehicleType]) - $[dailyRate]/day - [status description]"مع تنسيق dailyRate إلى منزلتين عشريتين.Car.java: أنشئ فئة ترث من Vehicle وتنفذ Rentable. أضف حقلاً خاصاًseats(int). يأخذ المنشئ licensePlate و brand و dailyRate و seats. قم بتنفيذgetVehicleType()لتعيد"Car". بالنسبة لـrent()، إذا كانت الحالة AVAILABLE، فقم بتغييرها إلى RENTED وأعد true؛ وإلا أعد false. بالنسبة لـreturnVehicle()، إذا كانت الحالة RENTED، فقم بتغييرها إلى AVAILABLE وأعد true؛ وإلا أعد false. بالنسبة لـcalculateCost(int days)، أعد حاصل ضرب dailyRate في days.Motorcycle.java: أنشئ فئة ترث من Vehicle وتنفذ Rentable. أضف حقلاً خاصاًengineCC(int). يأخذ المنشئ licensePlate و brand و dailyRate و engineCC. قم بتنفيذgetVehicleType()لتعيد"Motorcycle". قم بتنفيذ طرق Rentable بنفس طريقة Car، ولكن يجب أن تطبقcalculateCost()خصماً بنسبة 10% (اضرب في 0.9).RentalAgency.java: أنشئ فئة تدير أسطول المركبات باستخدام ArrayList. قم بتضمين الطرق التالية:addVehicle(Vehicle vehicle)- تضيف مركبة إلى الأسطولfindVehicle(String licensePlate)- تعيد المركبة (Vehicle) أو nullrentVehicle(String licensePlate, int days)- تبحث عن المركبة، وتحاول تأجيرها (بتحويلها إلى Rentable)، وتعيد تكلفة الإيجار. ارمِVehicleNotAvailableExceptionإذا لم يتم العثور على المركبة أو إذا أعادت rent() القيمة falsereturnVehicle(String licensePlate)- تبحث عن المركبة وتعيدها، وتعيد true عند النجاحgetAvailableVehicles()- تعيد ArrayList من المركبات التي حالتها AVAILABLE
Main.java: ابنِ خدمة التأجير الخاصة بك! ستتلقى سلسلة نصية من الأوامر مفصولة بفاصلة:ADD_CAR:licensePlate:brand:dailyRate:seatsADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCCRENT:licensePlate:daysRETURN:licensePlateAVAILABLE
قم بمعالجة كل أمر واطبع ما يلي:
- ADD_CAR/ADD_MOTORCYCLE:
Added: [brand] ([vehicleType]) - RENT:
Rented [licensePlate] for [days] days - Total: $[cost](التكلفة بمنزلتين عشريتين)، أوError: [exception message] - RETURN:
Returned: [licensePlate]أوReturn failed: [licensePlate] - AVAILABLE: اطبع
Available vehicles:متبوعة بتفاصيل كل مركبة متاحة في أسطر منفصلة
بعد معالجة جميع الأوامر، اطبع
--- Fleet Summary ---متبوعة بتفاصيل كل مركبة.
ستتلقى مدخلاً واحداً: سلسلة أوامر مفصولة بفاصلة.
على سبيل المثال، مع المدخل ADD_CAR:ABC-123:Toyota:50.00:5,ADD_MOTORCYCLE:XYZ-789:Honda:40.00:600,RENT:ABC-123:3,RENT:ABC-123:2,AVAILABLE,RETURN:ABC-123، سيكون مخرجك:
Added: Toyota (Car)
Added: Honda (Motorcycle)
Rented ABC-123 for 3 days - Total: $150.00
Error: Vehicle not available: ABC-123
Available vehicles:
XYZ-789 Honda (Motorcycle) - $40.00/day - ready to rent
Returned: ABC-123
--- Fleet Summary ---
ABC-123 Toyota (Car) - $50.00/day - ready to rent
XYZ-789 Honda (Motorcycle) - $40.00/day - ready to rentيجمع هذا التحدي بين الوراثة (هيكل المركبات)، والواجهات (Rentable)، والـ enums (RentalStatus)، والاستثناءات المخصصة، وتعدد الأشكال (التعامل مع أنواع المركبات المختلفة بشكل موحد)، والتركيب (RentalAgency التي تدير المركبات) في نظام خدمة تأجير كامل!
جرّب بنفسك
import java.util.Scanner;
import java.util.ArrayList;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: إنشاء مثيل (instance) من RentalAgency
// TODO: تقسيم المدخلات بواسطة الفاصلة للحصول على الأوامر الفردية
// TODO: معالجة كل أمر:
// ADD_CAR:licensePlate:brand:dailyRate:seats -> طباعة "Added: [brand] ([vehicleType])"
// ADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCC -> طباعة "Added: [brand] ([vehicleType])"
// RENT:licensePlate:days -> طباعة "Rented [licensePlate] for [days] days - Total: $[cost]" (بمنزلتين عشريتين)
// أو "Error: [exception message]" في حال حدوث استثناء (exception)
// RETURN:licensePlate -> طباعة "Returned: [licensePlate]" أو "Return failed: [licensePlate]"
// AVAILABLE -> طباعة "Available vehicles:" متبوعة بتفاصيل كل مركبة متاحة
// TODO: بعد جميع الأوامر، طباعة "--- Fleet Summary ---"
// متبوعة بتفاصيل كل مركبة
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المكرر