في هذا الدرس رح نبني تطبيق كامل لإدارة تأجير السيارات. التطبيق يخليك تضيف مركبة، تعمل Update وDelete عليها، وتستأجرها وتشوف اللي متاحة.
قبل ما نبلش، خلينا نشوف شو رح يكون المنيو:
=== Car Rental System ===
1. List Vehicles
2. Add Vehicle
3. Update Vehicle
4. Delete Vehicle
5. Rent Vehicle
6. Return Vehicle
7. List Rented Vehicles
8. Exit
Enter your choice:
النظام مبني على ثلاث كلاسات رئيسية:
| الكلاس | الدور |
|---|---|
Vehicle | الكلاس الأساسي (abstract) لكل المركبات |
RentalManager | يتحكم بكل عمليات التطبيق (orchestrator) |
RentalRecord | يحفظ سجل السيارات المستأجرة |
كلاسات Car وTruck ترث من Vehicle.
أهم مبدأ في بناء التطبيق: ابدأ بأبسط شي وأضف الخصائص تدريجياً. ما تبني كل شي وبعدين تكتشف إنه ما شغال.
نبدأ بكلاس Main اللي يبلش التطبيق:
public class Main {
public static void main(String[] args) {
RentalManager rentalManager = new
اشترك في النشرة البريدية
دروس جديدة، مقالات، وأدوات مباشرة لبريدك.
RentalManagerهذا الكلاس هو قلب التطبيق. يحمل Scanner لاستقبال الإدخال ومتغير hasStarted للتحكم بالـ while loop.
public class RentalManager {
private Scanner scanner = new Scanner(System.in);
private boolean hasStarted = true;
private List<Vehicle> vehicles = new ArrayList<>();
public void start() {
while (hasStarted) {
displayMenu();
int choice = scanner.nextInt();
scanner.nextLine();
switch (choice) {
case 1 -> listVehicles();
case 2 -> addVehicle();
case 3 -> updateVehicle();
case 4 -> deleteVehicle();
case 5 -> rentVehicle();
case 6 -> returnVehicle();
case 7 -> listRentedVehicles();
case 8 -> {
hasStarted = false;
System.out.println("Exiting system. Goodbye!");
}
}
}
}
}الـ while (hasStarted) يضمن إن التطبيق يضل شغال حتى يختار المستخدم Exit.
لازم نفصل عرض المنيو بـ method مستقلة لأن كل method لازم تساوي شغلة وحدة فقط، مبدأ Single Responsibility.
private void displayMenu() {
System.out.println("=== Car Rental System ===");
System.out.println("1. List Vehicles");
System.out.println("2. Add Vehicle");
System.out.println("3. Update Vehicle");
System.out.println("4. Delete Vehicle");
System.out.println("5. Rent Vehicle");
System.out.println("6. Return Vehicle");
System.out.println("7. List Rented Vehicles");
System.out.println("8. Exit");
System.out.print("Enter your choice: ");
}Vehicle - الـ Abstract ClassVehicle هو الأب لكل أنواع المركبات. يكون abstract لأنه مش رح ننشئ منه instance مباشرة.
خصائصه:
model - الموديلyear - سنة الإنتاجisRented - هل مؤجرة؟public abstract class Vehicle {
private String model;
private int year;
private boolean isRented;
public Vehicle(String model, int year) {
this.model = model;
this.year = year;
this.isRented = false; // افتراضياً مش مؤجرة
}
// Getters & Setters
public String getModel() { return model; }
public void setModel(String model) { this.model = model; }
public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
public boolean isRented() { return isRented; }
public void setRented(boolean rented) { isRented = rented; }isRented يبدأ بـ false لأن السيارة لما تُضاف بالأول ما تكون مؤجرة.
نضيف كمان method لتأجير وإرجاع السيارة مباشرة على الـ Vehicle:
public void rent() {
if (!isRented) {
isRented = true;
System.out.println(model + " has been rented.");
} else {
System.out.println(model + " is already rented.");
}
}
public void returnVehicle() {
if (isRented) {
isRented = false;
System.out.println(model + " has been returned.");
} else {
System.out.println(model + " was not rented.");
}
}
}Car وTruckكل نوع من المركبات يرث من Vehicle عن طريق extends:
public class Car extends Vehicle {
public Car(String model, int year) {
super(model, year);
}
}public class Truck extends Vehicle {
public Truck(String model, int year) {
super(model, year);
}
}كل الخصائص والـ methods المشتركة موجودة بالأب، فما في تكرار في الكود.
نستخدم Streams بدل الـ for loop العادية لنتدرب على الـ concepts:
private void listVehicles() {
vehicles.stream()
.map(v -> (vehicles.indexOf(v) + 1) + ". " + v.getModel()
+ " (" + v.getYear() + ") - "
+ (v.isRented() ? "Rented" : "Available"))
.forEach(System.out::println);
}لاحظ إننا نعطي كل سيارة رقم يبلش من 1 وليس من 0، لأن الـ index الأصلي يبدأ من صفر وهذا مش user-friendly.
1. Nissan (2015) - Available
2. Ford (2024) - Rentedنسأل المستخدم عن نوع المركبة أولاً، ثم الموديل والسنة:
private void addVehicle() {
System.out.print("Enter vehicle type (car/truck): ");
String type = scanner.nextLine().toLowerCase();
System.out.print("Enter model: ");
String model = scanner.nextLine();
System.out.print("Enter year: ");
int year = scanner.nextInt();
scanner.nextLine();
switch (type) {
case "car" -> vehicles.add(new Car(model, year));
case "truck" -> vehicles.add(new Truck(model, year));
default -> System.out.println("Invalid vehicle type.");
}
}نستخدم toLowerCase() مشان نتعامل مع الإدخال بغض النظر عن الـ case، سواء كتب المستخدم Car أو car أو CAR.
أول شي نعمل list للسيارات حتى يعرف المستخدم شو الأرقام المتاحة، ثم نطلب منه الرقم والقيم الجديدة:
private void updateVehicle() {
listVehicles();
System.out.print("Enter vehicle number to update: ");
int index = scanner.nextInt() - 1;
scanner.nextLine();
if (index >= 0 && index < vehicles.size()) {
Vehicle vehicle = vehicles.get(index);
System.out.print("Enter new model: ");
vehicle.setModel(scanner.nextLine());
System.out.print("Enter new year: ");
vehicle.setYear(scanner.nextInt());
scanner.nextLine();
} else {
System.out.println("Invalid selection.");
}
}التحقق index >= 0 && index < vehicles.size() يضمن إن المستخدم ما يدخل رقم سالب أو رقم أكبر من عدد السيارات الموجودة.
بعد ما نعرف الـ index، نستخدم vehicles.remove(index) مباشرة:
private void deleteVehicle() {
listVehicles();
System.out.print("Enter vehicle number to delete: ");
int index = scanner.nextInt() - 1;
scanner.nextLine();
if (index >= 0 && index < vehicles.size()) {
System.out.println(vehicles.get(index).getModel() + " has been deleted.");
vehicles.remove(index);
} else {
System.out.println("Invalid selection.");
}
}نأخذ رقم السيارة، نتحقق من صحة الـ index، ثم نستدعي rent() اللي موجودة على الـ Vehicle مباشرة:
private void rentVehicle() {
listVehicles();
System.out.print("Enter vehicle number to rent: ");
int index = scanner.nextInt() - 1;
scanner.nextLine();
if (index >= 0 && index < vehicles.size()) {
vehicles.get(index).rent();
} else {
System.out.println("Invalid selection.");
}
}إذا كانت السيارة مستأجرة بالفعل:
Nissan is already rented.نفس المنطق تماماً، بس نستدعي returnVehicle():
private void returnVehicle() {
listVehicles();
System.out.print("Enter vehicle number to return: ");
int index = scanner.nextInt() - 1;
scanner.nextLine();
if (index >= 0 && index < vehicles.size()) {
vehicles.get(index).returnVehicle();
} else {
System.out.println("Invalid selection.");
}
}هون نستخدم filter() على الـ stream لنفلتر بس السيارات اللي isRented فيها true:
private void listRentedVehicles() {
vehicles.stream()
.filter(Vehicle::isRented)
.map(Vehicle::getModel)
.forEach(System.out::println);
}النتيجة:
Nissan
Fordفي هذا المشروع مارسنا المفاهيم التالية:
stream()، filter()، map()، forEach()ArrayList، add()، remove()، get()الفكرة الأساسية: ابدأ بالهيكلية البسيطة وتأكد إن التطبيق شغال، ثم أضف الخصائص تدريجياً.