2

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?

  • Can you make a spreadsheet where rows are "fleet unit types" and columns are "properties that are valid for a particular type", to help understanding? You can post a screenshot via the stackoverflow imgur service. – rwong Jun 23 '15 at 23:34
2

Maybe i missunderstood the problem, but it looks like these are actually separated entities.

I would personally not use inherence but favor composition.

I would create different classes for each entity implementing an ITrackable interface and simply define the GPS as an object in these entities.

Using an Interface
An IReportable interface can also be defined and implemented to add a method to pass the reporting class. something as simple as

.report(reportbuilder r)
    { r.report(this) }

A visitor pattern should be kept in mind in case different reporters are required later on

  • If you want to combine them in a single list? – Papa-rapa-beo Jun 23 '15 at 21:58
  • sorry, i forgot to mention the Interface for polymorphism – eviloop Jun 23 '15 at 21:59
  • But what about the common features are id,alias,group, GpsDevice.... – Papa-rapa-beo Jun 23 '15 at 22:01
  • Are these GPS or Entity properties ? Maybe it makes sense to split it ? Do an abstract class for the entities but keep the GPS related stuff on composition ? – eviloop Jun 23 '15 at 22:02
  • It's possible... – Papa-rapa-beo Jun 23 '15 at 22:03
0

Personally, I would design a property bag of sorts on my base class implementation. It looks like you are using Java so pardon my C#, but I think the idea can still be conveyed.

public abstract class FleetUnit
{
    public IDictionary<string, object> Properties { get; set; }

    public FleetUnit()
    {
        Properties = new Dictionary<string, object>();
    }
}

public class Guard : FleetUnit
{
    protected GpsDevice Gps { get; private set; }

    public Guard(GpsDevice gps)
        : base()
    {
        Gps = gps;
        Properties.Add("Gps", gps);
    }
}

Accessing the properties from the base class becomes a TryGetValue call to the dictionary now.

public static GpsDevice GetGps(FleetUnit unit)
{
    GpsDevice gps = null;

    unit.Properties.TryGetValue("Gps", out gps);
    return gps; //If no property called Gps exists it returns null.
}

This allows you to deal with components of a FleetUnit without needing to know about the implementation of every type. The down side of course is you need to deal with units as a composition of different properties (Gps, Identity, so on)

You could alternatively use interfaces to describe the minimum of features or properties a unit requires to match a method signature which is the more pure OOP impementation, but that would require lots of additional methods to handle each interface and may end up with more complex branching (if-else, switch-case).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.