Menu
Coddy logo textTech

Сервис аренды транспорта

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 87 из 87.

Для этого финального испытания вы создадите систему Vehicle Rental Service, которая объединяет все концепции ООП, освоенные вами на протяжении этого курса.

Ваша система должна моделировать парк транспортных средств различных типов. Рассмотрите базовый класс Vehicle с общими свойствами, такими как номерной знак, марка и ежедневная стоимость аренды. Подклассы, такие как Car, Motorcycle и Truck, могут расширять его специфическими атрибутами, такими как количество мест, объем двигателя или грузоподъемность.

Интерфейсы могут определять поведение аренды: интерфейс Rentable может объявлять такие методы, как rent(), returnVehicle() и calculateCost(int days). Различные типы транспортных средств могут реализовывать стратегии ценообразования по-разному, что делает это идеальным примером использования полиморфизма.

Подумайте о том, как класс RentalAgency управляет автопарком, используя композицию.

Пользовательские исключения, такие как VehicleNotAvailableException, могут обрабатывать пограничные случаи.

Вы можете применить паттерн Factory для создания транспортных средств или использовать enums для категорий транспорта и статуса аренды.

Это испытание проверяет ваше умение проектировать полноценную, хорошо структурированную систему ООП. Удачи!

challenge icon

Задание

Легко

Давайте создадим полноценный сервис по прокату автомобилей (Vehicle Rental Service), который объединит все, что вы узнали об ООП! Вы создадите систему, которая управляет парком транспортных средств разных типов, обрабатывает аренду с надлежащей валидацией и использует паттерн «фабрика» для создания транспортных средств.

Вы организуете свой код в семи файлах:

  • RentalStatus.java: Создайте перечисление (enum), представляющее статус аренды транспортного средства. Включите значения AVAILABLE, RENTED и MAINTENANCE. Добавьте приватное поле description (String), конструктор и геттер. AVAILABLE должен иметь описание "ready to rent", RENTED — "currently in use", а MAINTENANCE — "under service".
  • VehicleNotAvailableException.java: Создайте пользовательское исключение, которое расширяет Exception. Конструктор должен принимать номерной знак (license plate) и создавать сообщение в формате Vehicle not available: [licensePlate].
  • Rentable.java: Создайте интерфейс, определяющий операции аренды. Объявите методы rent(), возвращающий boolean, returnVehicle(), возвращающий boolean, и calculateCost(int days), возвращающий double.
  • Vehicle.java: Создайте абстрактный базовый класс для всех транспортных средств. Включите приватные поля для licensePlate (String), brand (String), dailyRate (double) и status (RentalStatus, инициализированный как AVAILABLE). Конструктор принимает licensePlate, brand и dailyRate. Включите соответствующие геттеры и сеттер для status. Объявите абстрактный метод getVehicleType(), возвращающий String. Реализуйте метод getDetails(), который возвращает "[licensePlate] [brand] ([vehicleType]) - $[dailyRate]/day - [status description]", где dailyRate отформатирован до 2 знаков после запятой.
  • 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 или null
    • rentVehicle(String licensePlate, int days) — находит транспортное средство, пытается сдать его в аренду (приводя к Rentable) и возвращает стоимость аренды. Выбрасывает VehicleNotAvailableException, если транспортное средство не найдено или если rent() возвращает false
    • returnVehicle(String licensePlate) — находит и возвращает транспортное средство, возвращая true в случае успеха
    • getAvailableVehicles() — возвращает ArrayList транспортных средств со статусом AVAILABLE
  • Main.java: Постройте свой сервис аренды! Вы получите строку команд, разделенных запятыми:
    • ADD_CAR:licensePlate:brand:dailyRate:seats
    • ADD_MOTORCYCLE:licensePlate:brand:dailyRate:engineCC
    • RENT:licensePlate:days
    • RETURN:licensePlate
    • AVAILABLE

    Обработайте каждую команду и выведите:

    • ADD_CAR/ADD_MOTORCYCLE: Added: [brand] ([vehicleType])
    • RENT: Rented [licensePlate] for [days] days - Total: $[cost] (стоимость с 2 знаками после запятой) или 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

Эта задача объединяет наследование (иерархия Vehicle), интерфейсы (Rentable), перечисления (RentalStatus), пользовательские исключения, полиморфизм (единообразная работа с разными типами транспортных средств) и композицию (RentalAgency, управляющее транспортными средствами) в полноценную систему сервиса аренды!

Шпаргалка

Это финальное испытание объединяет несколько концепций ООП в полноценную систему. Вот основные паттерны и техники:

Перечисления (Enums) с полями

Перечисления могут иметь поля, конструкторы и методы:

public enum RentalStatus {
    AVAILABLE("ready to rent"),
    RENTED("currently in use");
    
    private String description;
    
    private RentalStatus(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
}

Пользовательские исключения

Создавайте специфичные для предметной области исключения, расширяя класс Exception:

public class VehicleNotAvailableException extends Exception {
    public VehicleNotAvailableException(String licensePlate) {
        super("Vehicle not available: " + licensePlate);
    }
}

Абстрактные базовые классы с интерфейсами

Комбинируйте абстрактные классы для общей реализации с интерфейсами для контрактов поведения:

public abstract class Vehicle {
    private String licensePlate;
    private RentalStatus status;
    
    public abstract String getVehicleType();
    
    public String getDetails() {
        return licensePlate + " " + getVehicleType() + " - " + status.getDescription();
    }
}

public interface Rentable {
    boolean rent();
    boolean returnVehicle();
    double calculateCost(int days);
}

public class Car extends Vehicle implements Rentable {
    public boolean rent() {
        if (getStatus() == RentalStatus.AVAILABLE) {
            setStatus(RentalStatus.RENTED);
            return true;
        }
        return false;
    }
}

Композиция для управления

Используйте композицию для управления коллекциями объектов:

public class RentalAgency {
    private ArrayList<Vehicle> fleet = new ArrayList<>();
    
    public void addVehicle(Vehicle vehicle) {
        fleet.add(vehicle);
    }
    
    public double rentVehicle(String licensePlate, int days) throws VehicleNotAvailableException {
        Vehicle vehicle = findVehicle(licensePlate);
        if (vehicle == null) {
            throw new VehicleNotAvailableException(licensePlate);
        }
        Rentable rentable = (Rentable) vehicle;
        if (!rentable.rent()) {
            throw new VehicleNotAvailableException(licensePlate);
        }
        return rentable.calculateCost(days);
    }
}

Полиморфизм в действии

Различные подклассы могут реализовывать методы интерфейса по-разному:

// Car: стандартное ценообразование
public double calculateCost(int days) {
    return dailyRate * days;
}

// Motorcycle: ценообразование со скидкой
public double calculateCost(int days) {
    return dailyRate * days * 0.9;
}

Попробуйте сами

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: Создайте экземпляр 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]" (2 знака после запятой)
        //                           или "Error: [exception message]", если возникло исключение
        // RETURN:licensePlate -> выведите "Returned: [licensePlate]" или "Return failed: [licensePlate]"
        // AVAILABLE -> выведите "Available vehicles:" и сведения о каждом доступном транспортном средстве
        
        // TODO: После выполнения всех команд выведите "--- Fleet Summary ---"
        // за которым следуют сведения о каждом транспортном средстве
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование