I am developing a web based application which is about tracking Fleet units.
Actually i have already developed the system but in the last six years things changed a bit. Ordinary the software specification was about monitoring vehicles in real time and then doing various reports.
How ever during the years we got so many clients in Europe that some of them wanted custom behaviour like inputing the date when various licenses were bought and the date when they expirse so they get a notification for each vehicle when it's time to buy new licence, vignette, insurance and so on..
All seemed to be very good until the time some clients decided that they will buy only 100 GPS devices but they still want to have the rest of 27 (for example) vehicles entered into the system so they get notifications about the vignette, insurance and licence expiration dates. So I had to add new variable like
boolean hasGps; Things got even worse. Some clients wanted to attach GPS devices to the trailers of the vehicles so they can monitor them to. So the only way to make this possible is to register the trailers as Vehicles and use the same class where i had to add a new field variable like:
boolean isTrailer; Well... again things got really ugly when a company liked the flow meter system that we attach to the vehicles and keep track of the spent fuel so they wanted to have a GPS device and a flow meter device integrated in their GAS station. Again i had to add a new variable like:
boolean isGasTank; These days the boss decided that the company will start selling personal tracking devices for security guards and dogs.
So... I decided to re-write the business logic and have the code more clean. However in a real world scenario it seems really difficult to make a good OOP design.
Lets say we have the following fleet units types where a gps device can be attached:
Vehicle, Trailer, GasStationTank, Ship, Person, Animal
All of this units have no behaviour. They only has a state and definition fields.
The only exception where a gps device may not be attached is the Vehicle object because as i mentioned we enter some ghost vehicles where the only tracking is about the expiration dates of various licences, vignette and insurance.
The Vehicle class has most fields where as the other classes have only id and alias fields. Some of the vehicle's fields are:
maxSpeed, fuelPer100, revolutions, flowMeter, fuelNorms. tanks, digitalEvents, analogEvents, parkingDefinition, workingDefinition,
Here is what I have made so far:
public enum FleetUnitType { VEHICLE, TRAILER, NONE_GPS_UNIT, GAS_STATTION_TANK, MOVING_NON_VEHICLE_UNIT } Abstract class
public abstract class FleetUnit { private final String id; private String alias; private String group; private Optional<GpsDevice> gpsDevice; private long dateTimeAddedToSystem; private FleetUnit linkedFleetUnit; public FleetUnit(String id, String alias, String group) { if(id == null) throw new NullPointerException(); else { this.id = id; } this.alias = (alias == null ? id : alias); this.group = group; } public abstract FleetUnitType getType(); } Vehicle class that implements FleetUnit class
public class Vehicle extends FleetUnit { public static final double VBAT_CONST = 0.175; private int maxSpeed; private double urbanFuel100; private double suburbanFuel100; private double fuelPerHour; double maxAccumulatorValueForZeroRevolution; private Optional<Revolutions> revolutions; private Optional<FlowMeter> flowMeter; private Optional<FuelNorm> fuelNorm; private final Map<Integer, FuelTank> fuelTanks; private final Map<Integer, DigitalEvent> digitalEvents; private ParkingDefinition parkingStartDefinition; private WorkingDefinition workingStartDefinition; public Vehicle(String id, String alias, String group, VehicleMetaData vMetaData) { super(id, alias, group); this.fuelTanks = new HashMap<>(); this.digitalEvents = new HashMap<>(); parkingStartDefinition = ParkingDefinition.ENGINE_OFF; workingStartDefinition = WorkingDefinition.ENGINE_ON; } @Override public FleetUnitType getType() { if(super.getGpsDevice().isPresent()) return FleetUnitType.VEHICLE; return FleetUnitType.NONE_GPS_DEVICE; } public static enum EngineState { MOVING, PARKED, STOPPED, WORKING, CONTACT, UNKNOWN } public static enum ParkingDefinition { ENGINE_OFF, TACHOGRAPH } public static enum WorkingDefinition { ENGINE_ON, MIN_DISTANCE_TRAVELED } public static enum MotoHoursCalculation { ACCUMULATOR, KANSHINA } } As you can see these objects has no behaviour but simply a state which is useful for various reports like how much distance is traveled today or what is the spent fuel and so on...
So finally the question is, should I keep this design and then make the rest of the concrete classes to implement FleetUnit class or I should do something completely different?