Сервис аренды транспорта
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 87 из 87.
Для этого финального испытания вы создадите систему Vehicle Rental Service, которая объединяет все концепции ООП, освоенные вами на протяжении этого курса.
Ваша система должна моделировать парк транспортных средств различных типов. Рассмотрите базовый класс Vehicle с общими свойствами, такими как номерной знак, марка и ежедневная стоимость аренды. Подклассы, такие как Car, Motorcycle и Truck, могут расширять его специфическими атрибутами, такими как количество мест, объем двигателя или грузоподъемность.
Интерфейсы могут определять поведение аренды: интерфейс Rentable может объявлять такие методы, как rent(), returnVehicle() и calculateCost(int days). Различные типы транспортных средств могут реализовывать стратегии ценообразования по-разному, что делает это идеальным примером использования полиморфизма.
Подумайте о том, как класс RentalAgency управляет автопарком, используя композицию.
Пользовательские исключения, такие как VehicleNotAvailableException, могут обрабатывать пограничные случаи.
Вы можете применить паттерн Factory для создания транспортных средств или использовать enums для категорий транспорта и статуса аренды.
Это испытание проверяет ваше умение проектировать полноценную, хорошо структурированную систему ООП. Удачи!
Задание
ЛегкоДавайте создадим полноценный сервис по прокату автомобилей (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или 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](стоимость с 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 ---"
// за которым следуют сведения о каждом транспортном средстве
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy14Финальные испытания
Платформа онлайн-обученияБанковская системаРазработка игрового персонажаСервис аренды транспорта3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»