From d01f6fffd3d5f21bad5bef44effcd0f3aabee209 Mon Sep 17 00:00:00 2001 From: arnaudlabourel <arnaud.labourel@univ-amu.fr> Date: Thu, 8 Sep 2022 10:49:00 +0200 Subject: [PATCH] Correction composition --- settings.gradle | 2 +- src/main/java/agency/AbstractVehicle.java | 2 +- src/main/java/agency/CarSpecifics.java | 43 ++++++ src/main/java/agency/Client.java | 1 - src/main/java/agency/ConcreteVehicle.java | 73 ++++++++++ src/main/java/agency/MotorbikeSpecifics.java | 30 ++++ src/main/java/agency/RentalAgency.java | 12 +- .../java/agency/VehicleTypeSpecifics.java | 7 + src/test/java/agency/TestRentalAgency.java | 130 +++++++++++------- 9 files changed, 244 insertions(+), 56 deletions(-) create mode 100644 src/main/java/agency/CarSpecifics.java create mode 100644 src/main/java/agency/ConcreteVehicle.java create mode 100644 src/main/java/agency/MotorbikeSpecifics.java create mode 100644 src/main/java/agency/VehicleTypeSpecifics.java diff --git a/settings.gradle b/settings.gradle index b96841a..0ab41f9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'formula' +rootProject.name = 'agency' diff --git a/src/main/java/agency/AbstractVehicle.java b/src/main/java/agency/AbstractVehicle.java index 03f016f..2ad1427 100644 --- a/src/main/java/agency/AbstractVehicle.java +++ b/src/main/java/agency/AbstractVehicle.java @@ -33,8 +33,8 @@ public abstract class AbstractVehicle implements Vehicle { if(productionYear < MINIMAL_YEAR_VALUE || productionYear > currentYearValue) throw new IllegalArgumentException(productionYear + " is not a correct year (comprised between " + MINIMAL_YEAR_VALUE + " and " + currentYearValue + ")"); - this.brand = brand; this.productionYear = productionYear; + this.brand = brand; this.model = model; } diff --git a/src/main/java/agency/CarSpecifics.java b/src/main/java/agency/CarSpecifics.java new file mode 100644 index 0000000..cb5fff3 --- /dev/null +++ b/src/main/java/agency/CarSpecifics.java @@ -0,0 +1,43 @@ +package agency; + +import util.TimeProvider; + +public class CarSpecifics implements VehicleTypeSpecifics { + private static final int MAXIMUM_AGE_FOR_NEW_CAR = 5; + private final int numberOfSeats; + private static final int PRICE_PER_SEAT_FOR_NEW_CAR = 40; + private static final int PRICE_PER_SEAT_FOR_OLD_CAR = 20; + private final boolean isNew; + + public CarSpecifics(int numberOfSeats, int productionYear) { + isNew = age(productionYear) <= MAXIMUM_AGE_FOR_NEW_CAR; + this.numberOfSeats = numberOfSeats; + if (numberOfSeats < 1) + throw new IllegalArgumentException("Cannot construct a car with " + + numberOfSeats + " seats"); + } + + private static int age(int productionYear) { + return TimeProvider.currentYearValue() - productionYear; + } + + @Override + public double dailyRentalPrice() { + final int priceBySeat = isNew ? PRICE_PER_SEAT_FOR_NEW_CAR : PRICE_PER_SEAT_FOR_OLD_CAR; + return priceBySeat * numberOfSeats; + } + + @Override + public String kindOfVehicle() { + return "Car"; + } + + @Override + public String vehicleDetails() { + return numberOfSeats + " " + (hasSeveralSeats() ? "seats" : "seat"); + } + + private boolean hasSeveralSeats() { + return numberOfSeats > 1; + } +} diff --git a/src/main/java/agency/Client.java b/src/main/java/agency/Client.java index 65fdab4..5d8b003 100644 --- a/src/main/java/agency/Client.java +++ b/src/main/java/agency/Client.java @@ -6,7 +6,6 @@ public class Client { private final String firstName; private final String lastName; private final int yearOfBirth; - public Client(String firstName, String lastName, int yearOfBirth) { this.firstName = firstName; this.lastName = lastName; diff --git a/src/main/java/agency/ConcreteVehicle.java b/src/main/java/agency/ConcreteVehicle.java new file mode 100644 index 0000000..a637e46 --- /dev/null +++ b/src/main/java/agency/ConcreteVehicle.java @@ -0,0 +1,73 @@ +package agency; + +import util.TimeProvider; + +public class ConcreteVehicle implements Vehicle{ + private static final int MINIMAL_YEAR_VALUE = 1900; + private final String brand; + private final String model; + private final int productionYear; + private final VehicleTypeSpecifics vehicleTypeSpecifics; + + @Override + public int hashCode() { + int result = brand.hashCode(); + result = 31 * result + model.hashCode(); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || this.getClass() != o.getClass()) return false; + + ConcreteVehicle that = (ConcreteVehicle) o; + if (vehicleTypeSpecifics.getClass() != that.vehicleTypeSpecifics.getClass()) + return false; + if (!brand.equals(that.brand)) return false; + return model.equals(that.model); + } + + ConcreteVehicle(String brand, String model, int productionYear, + VehicleTypeSpecifics vehicleTypeSpecifics) { + int currentYearValue = TimeProvider.currentYearValue(); + if(productionYear < MINIMAL_YEAR_VALUE || productionYear > currentYearValue) + throw new IllegalArgumentException(productionYear + " is not a correct year (comprised between " + + MINIMAL_YEAR_VALUE + " and " + currentYearValue + ")"); + this.productionYear = productionYear; + this.brand = brand; + this.model = model; + this.vehicleTypeSpecifics = vehicleTypeSpecifics; + } + + @Override + public String getBrand() { + return brand; + } + + @Override + public String getModel() { + return model; + } + @Override + public int getProductionYear() { + return productionYear; + } + public String kindOfVehicle(){ + return vehicleTypeSpecifics.kindOfVehicle(); + } + public String vehicleDetails(){ + return vehicleTypeSpecifics.vehicleDetails(); + } + @Override + public double dailyRentalPrice() { + return vehicleTypeSpecifics.dailyRentalPrice(); + } + + @Override + public String toString() { + return kindOfVehicle() + " " + brand + " " + model + " " + productionYear + + " (" + vehicleDetails() + ")" + " : " + + dailyRentalPrice() + "€"; + } +} diff --git a/src/main/java/agency/MotorbikeSpecifics.java b/src/main/java/agency/MotorbikeSpecifics.java new file mode 100644 index 0000000..dbde698 --- /dev/null +++ b/src/main/java/agency/MotorbikeSpecifics.java @@ -0,0 +1,30 @@ +package agency; + +public class MotorbikeSpecifics implements VehicleTypeSpecifics{ + private static final double PRICE_PER_UNIT_OF_CYLINDER_CAPACITY = 0.25; + private static final int MINIMAL_CAPACITY = 50; + private static final String UNIT_OF_CYLINDER_CAPACITY = "cm³"; + private final int cylinderCapacity; + + + MotorbikeSpecifics(int cylinderCapacity) { + if (!(cylinderCapacity < MINIMAL_CAPACITY)) + throw new IllegalArgumentException("Incorrect cylinder capacity "+ cylinderCapacity + "(must be at least " + MINIMAL_CAPACITY +")"); + this.cylinderCapacity = cylinderCapacity; + } + + @Override + public String kindOfVehicle() { + return "Motorbike"; + } + + @Override + public String vehicleDetails() { + return cylinderCapacity + UNIT_OF_CYLINDER_CAPACITY; + } + + @Override + public double dailyRentalPrice() { + return cylinderCapacity * PRICE_PER_UNIT_OF_CYLINDER_CAPACITY; + } +} diff --git a/src/main/java/agency/RentalAgency.java b/src/main/java/agency/RentalAgency.java index 62a52ab..e49883e 100644 --- a/src/main/java/agency/RentalAgency.java +++ b/src/main/java/agency/RentalAgency.java @@ -7,6 +7,13 @@ public class RentalAgency { private final List<Vehicle> vehicles = new ArrayList<>(); private final Map<Client, Vehicle> rentedVehicles = new HashMap<>(); + public RentalAgency() { + } + + public RentalAgency(List<Vehicle> vehicles) { + this.vehicles.addAll(vehicles); + } + void remove(Vehicle vehicle){ requiresExisting(vehicle); vehicles.remove(vehicle); @@ -81,9 +88,12 @@ public class RentalAgency { return vehicles; } - public void printVehicles(){ + private static void print(List<Vehicle> vehicles){ for(Vehicle vehicle : vehicles){ System.out.println(vehicle); } } + public void printSelectedVehicles(Predicate<Vehicle> criterion){ + print(select(criterion)); + } } diff --git a/src/main/java/agency/VehicleTypeSpecifics.java b/src/main/java/agency/VehicleTypeSpecifics.java new file mode 100644 index 0000000..0da90b4 --- /dev/null +++ b/src/main/java/agency/VehicleTypeSpecifics.java @@ -0,0 +1,7 @@ +package agency; + +public interface VehicleTypeSpecifics { + String kindOfVehicle(); + String vehicleDetails(); + double dailyRentalPrice(); +} diff --git a/src/test/java/agency/TestRentalAgency.java b/src/test/java/agency/TestRentalAgency.java index 7c53317..227aa2f 100644 --- a/src/test/java/agency/TestRentalAgency.java +++ b/src/test/java/agency/TestRentalAgency.java @@ -2,92 +2,118 @@ package agency; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import util.TimeProvider; + +import java.time.LocalDateTime; +import java.util.Objects; import static org.assertj.core.api.Assertions.*; class TestRentalAgency extends TestCar{ - private RentalAgency rentalAgency; + + Car catalinaCar = new Car(apple, catalina, catalinaYear, catalinaNumberOfSeats); + Car windows95Car = new Car(microsoft, windows95, windows95Year, windows95NumberOfSeats); + static final String apple = "Apple"; + static final String catalina = "Catalina"; + static final String microsoft = "Microsoft"; + static final String windows95 = "Windows95"; + static final int catalinaYear = 2019; + static final int windows95Year = 1995; + static final int catalinaNumberOfSeats = 3; + private static final int windows95NumberOfSeats = 1; + private static final LocalDateTime YEAR2000 = LocalDateTime.of(2000, 1, 1, 0, 0); + private static final LocalDateTime YEAR2019 = LocalDateTime.of(2019, 1, 1, 0, 0); @BeforeEach - void initializeRentalAgency(){ - rentalAgency = new RentalAgency(); + void fixClock(){ + TimeProvider.useFixedClockAt(YEAR2019); } @Test - void testAddingCars(){ - rentalAgency.add(catalinaCar); - assertThat(rentalAgency.getVehicles()) - .containsExactly(catalinaCar); - rentalAgency.add(windows95Car); - assertThat(rentalAgency.getVehicles()) - .containsExactly(catalinaCar, windows95Car); + void testConstructionOfTooOldVehicle(){ + assertThatIllegalArgumentException() + .isThrownBy(()->new Car("Linux", "ubuntu",1899, 3)) + .withMessageContaining("1899"); } @Test - void testAddingSameCar() { - assertThat(rentalAgency.add(catalinaCar)).isTrue(); - assertThat(rentalAgency.add(catalinaCar)).isFalse(); + void testConstructionOfFutureVehicle() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new Car("Linux", "ubuntu", 2020, 3)) + .withMessageContaining("2020"); } @Test - void testRemovingExistingVehicle(){ - rentalAgency.add(catalinaCar); - rentalAgency.remove(catalinaCar); - assertThat(rentalAgency.getVehicles()).doesNotContain(catalinaCar); + void testConstructionOfVehicleWithNoSeats() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new Car("Linux", "ubuntu", 2000, 0)) + .withMessageContaining("0"); } @Test - void testRemovingNonExistingVehicle(){ - assertThatThrownBy(() -> rentalAgency.remove(catalinaCar)) - .isInstanceOf(UnknownVehicleException.class) - .hasMessageContaining(catalinaCar.toString()); + void testGetters() { + assertThat(catalinaCar).hasFieldOrPropertyWithValue("brand", apple) + .hasFieldOrPropertyWithValue("model", catalina) + .hasFieldOrPropertyWithValue("productionYear", catalinaYear) + .hasFieldOrPropertyWithValue("numberOfSeats", catalinaNumberOfSeats); } @Test - void testSelect(){ - rentalAgency.add(catalinaCar); - rentalAgency.add(windows95Car); - assertThat(rentalAgency.select(vehicle -> vehicle.getBrand().equals(apple))) - .containsExactly(catalinaCar); - assertThat(rentalAgency.select(vehicle -> vehicle.getModel().equals(windows95))) - .containsExactly(windows95Car); + void testConstructionOfFutureCar(){ + assertThatIllegalArgumentException(). + isThrownBy(() -> new Car("Linux", "ubuntu", + 2020, 2)); } - - - private final Client arnaud = new Client("Arnaud", "Labourel", 1981); - private final Client paul = new Client("Paul", "Calcul", 2018); - @Test - void testRentingVehicles(){ - rentalAgency.add(catalinaCar); - rentalAgency.add(windows95Car); - rentalAgency.rentVehicle(arnaud, catalinaCar); - rentalAgency.rentVehicle(paul, windows95Car); - assertThat(rentalAgency.allRentedVehicles()).containsExactly(catalinaCar, windows95Car); + void testConstructionOfTooOldCar(){ + assertThatIllegalArgumentException(). + isThrownBy(() -> new Car("Linux", "ubuntu", + 1899, 1)); } @Test - void testSameClientRentingSeveralVehicles(){ - rentalAgency.add(catalinaCar); - rentalAgency.add(windows95Car); - assertThat(rentalAgency.rentVehicle(arnaud, catalinaCar)).isEqualTo(120.); - assertThatIllegalStateException().isThrownBy(() -> rentalAgency.rentVehicle(arnaud, windows95Car)); + void testThatCarImplementsVehicle(){ + assertThat(catalinaCar).withFailMessage("%s must implement %s", + Car.class, Vehicle.class) + .isInstanceOf(Vehicle.class); } @Test - void testSameVehicleRentedTwice(){ - rentalAgency.add(catalinaCar); - rentalAgency.rentVehicle(arnaud, catalinaCar); - assertThatIllegalStateException().isThrownBy(() -> rentalAgency.rentVehicle(paul, catalinaCar)); + void testIsNew(){ + assertThat(windows95Car).withFailMessage("A car with production year %s should have been old", + windows95Year) + .returns(false, Car::isNew); + assertThat(catalinaCar).withFailMessage("A car with production year %s should have been new", + catalinaYear) + .returns(true, Car::isNew); + TimeProvider.useFixedClockAt(YEAR2000); + assertThat(windows95Car).withFailMessage("A car with production year %s should have been new in %s", + windows95Year, YEAR2000) + .returns(true, Car::isNew); + TimeProvider.useFixedClockAt(YEAR2019); } + @Test + void testDailyRentalPrice(){ + double expectedPriceCatalinaCar = 40.*catalinaNumberOfSeats; + double expectedPriceWindowsCar = 20.*windows95NumberOfSeats; + assertThat(catalinaCar).as("Car DailyRentalPrice") + .withFailMessage("A new car with %s seats must have price %s", + catalinaNumberOfSeats, expectedPriceCatalinaCar ) + .returns(expectedPriceCatalinaCar, Car::dailyRentalPrice); + assertThat(windows95Car).as("Car DailyRentalPrice") + .withFailMessage("An old car with %s seat must have price %s", + windows95NumberOfSeats, expectedPriceCatalinaCar ) + .returns(expectedPriceWindowsCar, Car::dailyRentalPrice); + } @Test - void testRentingAVehicleNotInTheAgency(){ - assertThatThrownBy(() -> rentalAgency.rentVehicle(paul, catalinaCar)) - .isInstanceOf(UnknownVehicleException.class) - .hasMessageContaining(catalinaCar.toString()); + void testToString(){ + String expectedStringMacOs = "Car Apple Catalina 2019 (3 seats) : 120.0€"; + assertThat(catalinaCar).returns(expectedStringMacOs, Objects::toString); + String expectedStringWin = "Car Microsoft Windows95 1995 (1 seat) : 20.0€"; + assertThat(windows95Car).returns(expectedStringWin, Objects::toString); } } -- GitLab