Lab-06 VehicleFleet

1. UML-Diagram

mypkgexceptionvehicleifaceInventoryCodenextInvtNr: intinvtCodeTxt: StringConstructor(invtCodeTxt)generate4kind(invtPrefix: String): InventoryCodecheckValidKindPrefix(kindPrefix: String)checkValid(invtCodeTxt: String) checkValidFor(vehicle: Vehicle) hashCode(): intequals(Object obj): boolean toString(): StringVehicleFleetvehiclesMap: Map<InventoryCode,Vehicle>Constructor() vehiclesCount(): intgetVehicle(invtCode: InventoryCode): Vehicle add(vehicle: Vehicle)remove(invtCode: InventoryCode): Vehicle findMotorDrivenVehicles(): Set<Vehicle>totalTransportCapacity(): intfindVehiclesJoungerThan(someDate: LocalDate): List<Vehicle> printVehicles(title: String, vehicles Collection<Vehicle>)BadInvtCodeExceptionConstructors(..)BadInvtKindPrefixExceptionConstructors(..)DuplicateVehicleExceptionConstructors(..)BadParamExceptionConstructors(..)AppBugRtExceptionConstructors(..)ChangeInvtCodeExceptionConstructors(..)VehicleinvtCode: InventoryCodesince: LocalDateinfo: StringemptyWeight: intConstructor(invtCode: InventoryCode,since: LocalDate, info: String, emptyWeight: int)getters(): TheTypesetInvtCode(invtCode: InventoryCode)other_setters(param: TheType) hashCode(): intequals(obj: Object) toString(): StringCarpower: intConstructor(invtCode: InventoryCode, since: LocalDate,info: String, emptyWeight: int, power: int) toString(): StringTruckpower: intloadCapacity: int // kgConstructor(invtCode: InventoryCode, since: LocalDate,info: String, emptyWeight: int, power: int, loadCapacity: int) toString(): StringTrailerprivate int loadCapacity; // kgConstructor(invtCode: InventoryCode, since: LocalDate,info: String, emptyWeight: int, loadCapacity: int) toString(): StringMotorDrivengetPower(): intGoodsTransportinggetLoadCapacity(): intcheckValid(..)«throws»checkValidKindPrefix(..)checkValidFor(..)«throws»add(..)«throws»add(..)«throws»add(..)«throws»all setters butsettInvtCode«throws»setInvtCode(..)«throws»Car, Truck, Trailerall setters «throws»

2. Additional Description

We have a VehicleFleet administration app, holding items of Car, Truck and Trailer. All of them are specializations of abstract Vehicle.

Every Vehicle has a unique InventoryCode as soon as it becomes included into the fleet. Before that the InventoryCode can be null or is set at some moment. After setting it is not changeable – the setter has to throw a ChangeInvtCodeException.

The invtCodeTxt consists of 3 Letters labelling the kind (Car → CAR, Truck → TRU, Trailer → TRA) and a 5-digits number, which is automatically incremented after being "consumed" (done within generate4kind()).

Ensure the number is filled with '0’s if less than 5 digits (see example below).

The inventoryCode Objects (Class InventoryCode) hold the invtCodeTxt and some "intelligence" via methods:

  • Static factory method InventoryCode generate4kind(String invtPrefix) throws …​ …​ calls static method void checkValidKindPrefix(String invtPrefix). The latter does the checks for validity (one of 'CAR', 'TRU', 'TRA') and throws a BadInvtKindPrefixException if needed (does nothing otherwise!).

  • Static method void checkValid(String invtCodeTxt) is provided to check a given complete invtCodeTxt for validity. Throws BadInvtCodeException in case of invalidity: invalid prefix, rest not exactly 5 digits.

  • Instance method void checkValidFor(Vehicle vehicle) is able to check if a given InventoryCode object is suitable to be used for the kind of the given vehicle (parameter).

    It uses: if (vehicle instanceof Car car) { // prefix 'CAR', …​}.

  • Method equals(..) of the key objects is used extensively in collections – e.g. Map-methods get(..) containsKey(..), remove(..), …​ . As soon as we use hashes behind the scenes, hashCode() is used too and it is EXTREMELY important that it’s definition is consistent with equals(..), since these work together then. So auto-generation (Menu Source → generate hashCode() and equals() …​) in Eclipse/IntelliJ should be used.

Method add(..) has to set the InventoryCode value invtCode if it is null to ensure the Vehicle has the needed Map key attribute.

To find out if an object is member of a certain class or interface, use:

    // ...
    if (someObj instanceof SomeClassOrIface) {
        ScomeClassOrIface scoi = (ScomeClassOrIface) someObj;  // obsolete in Java16
        scoi.someMethod();
        //...
    }
    // or better since Java 16:
    if (someObj instanceof SomeClassOrIface scoi) {
        scoi.someMethod();
        // ...
    }
    // ...
public class InventoryPrefix {
    // ...
    public static InventoryCode generate4kind(String kindPrefix)
            throws BadInvtKindPrefixException {
        checkValidKindPrefix(kindPrefix);
        // %05d ensures 5 digits are here, missing ones filled with '0's:
        return new InventoryCode("%s%05d".formatted(kindPrefix, nextInvtNr++));
    }

    public static void checkValid(String invtCodeTxt) throws BadInvtCodeException {
        // ...
    }

    // ...
}

In VehicleFleet the Exceptions BadInvtKindPrefixException, BadParamException and ChangeInvtCodeException can only happen if the app logic is incorrect - so we catch these and rethrow an AppBugRtException("InvtCode Bug? %s".formatted(invtCode), ex), giving the original exception as second param ex.

Method VehicleFleet#checkValidFor(Vehicle vehicle) has to be called before the InventoryCode is added to the Vehicle - cound be done in setter of Vehicle or externally within the VehicleFleet add(..) method. Should throw BadInvtKindPrefixException, if the wrong prefix for the given vehicle is set.

The Car, Truck and Trailer classes need a overridden toString() which has to use the one of the superclass as the first part. Subclass specific attributes have to be added behind.
Format should be chosen in a way that extending is possible – so let away any brackets! To use dynamic class name creation use
this.getClass().getSimpleName()
which provides the real class instead of a statically written one!

Do some simple testing e.g. within a main method or by a separate test class.