Has-A o Is-A o qualche schema di strategia?

2

Sto sviluppando un'applicazione basata sul web che tratta il monitoraggio delle unità di flotta.

In realtà ho già sviluppato il sistema ma negli ultimi sei anni le cose sono cambiate un po '. Normalmente le specifiche del software riguardavano il monitoraggio dei veicoli in tempo reale e quindi l'esecuzione di vari rapporti.

Come mai nel corso degli anni abbiamo avuto così tanti clienti in Europa che alcuni di loro volevano un comportamento personalizzato come inserire la data in cui sono state acquistate varie licenze e la data in cui scadono in modo da ricevere una notifica per ogni veicolo quando è il momento di acquistare nuova licenza, vignetta, assicurazione e così via ..

Tutto sembrava essere molto buono fino al momento in cui alcuni clienti hanno deciso di acquistare solo 100 dispositivi GPS, ma vogliono comunque che il resto dei 27 (ad esempio) veicoli entrino nel sistema in modo da ricevere notifiche sulla vignetta, date di scadenza di assicurazione e licenza. Quindi ho dovuto aggiungere una nuova variabile come

boolean hasGps;

Le cose sono andate ancora peggio. Alcuni clienti volevano collegare dispositivi GPS ai rimorchi dei veicoli in modo che possano monitorarli. Quindi l'unico modo per rendere ciò possibile è registrare i rimorchi come veicoli e utilizzare la stessa classe in cui ho dovuto aggiungere una nuova variabile di campo come:

boolean isTrailer;

Bene ... ancora una volta le cose sono diventate davvero brutte quando a un'azienda piaceva il sistema di misuratori di portata che colleghiamo ai veicoli e teniamo traccia del combustibile esaurito in modo che volessero avere un dispositivo GPS e un dispositivo misuratore di portata integrati nel loro GAS stazione. Ancora una volta ho dovuto aggiungere una nuova variabile come:

boolean isGasTank;

In questi giorni il capo ha deciso che la società inizierà a vendere dispositivi di localizzazione personali per guardie e cani da guardia.

Quindi ... ho deciso di riscrivere la logica del business e avere il codice più pulito. Tuttavia, in uno scenario reale sembra davvero difficile realizzare un buon design OOP.

Diciamo che abbiamo i seguenti tipi di unità di flotta in cui è possibile collegare un dispositivo GPS:

Veicolo, rimorchio, GasStationTank, nave, persona, animale

Tutte queste unità non hanno alcun comportamento. Hanno solo uno stato e campi di definizione.

L'unica eccezione in cui un dispositivo GPS non può essere collegato è l'oggetto Veicolo perché, come accennato, inseriamo alcuni veicoli fantasma in cui l'unico rilevamento riguarda le date di scadenza di varie licenze, vignette e assicurazioni.

La classe Vehicle ha la maggior parte dei campi dove le altre classi hanno solo id e alias . Alcuni dei campi del veicolo sono:

maxSpeed, fuelPer100, giri, misuratore di flusso, fuelNorms. serbatoi, digitaliEventi, analogEventi, parkingDefinition, workingDefinition,

Ecco cosa ho fatto finora:

public enum FleetUnitType {

    VEHICLE, TRAILER, NONE_GPS_UNIT, GAS_STATTION_TANK, MOVING_NON_VEHICLE_UNIT

}

Classe astratta

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();


}

Classe del veicolo che implementa la classe FleetUnit

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
    }

}

Come puoi vedere, questi oggetti non hanno un comportamento ma semplicemente uno stato che è utile per vari rapporti come la distanza percorsa oggi o il combustibile esaurito e così via ...

Quindi alla fine la domanda è: dovrei mantenere questo disegno e poi fare il resto delle lezioni concrete per implementare la classe FleetUnit o dovrei fare qualcosa di completamente diverso?

    
posta Papa-rapa-beo 19.06.2015 - 17:42
fonte

2 risposte

2

Forse ho capito male il problema, ma sembra che siano entità separate.

Personalmente non userò l'ereditarietà ma preferisco la composizione.

Creo classi diverse per ogni entità che implementa un'interfaccia ITrackable e definisco semplicemente il GPS come un oggetto in queste entità.

Utilizzo di un'interfaccia
Un'interfaccia IReportable può anche essere definita e implementata per aggiungere un metodo per passare la classe di reporting. qualcosa di semplice come

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

Un modello di visitatore dovrebbe essere tenuto presente nel caso in cui in seguito siano richiesti diversi reporter

    
risposta data 23.06.2015 - 23:54
fonte
0

Personalmente, progetterei un pacchetto di proprietà di sorta sulla mia implementazione di classe base. Sembra che tu stia usando Java per perdonare il mio C #, ma penso che l'idea possa ancora essere trasmessa.

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);
    }
}

L'accesso alle proprietà dalla classe base diventa una chiamata TryGetValue al dizionario ora.

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.
}

Questo ti consente di gestire i componenti di un FleetUnit senza bisogno di conoscere l'implementazione di ogni tipo. Il lato negativo, naturalmente, è che devi gestire unità come una composizione di proprietà diverse (Gps, Identità, ecc.)

In alternativa, puoi utilizzare le interfacce per descrivere il minimo di caratteristiche o proprietà che un'unità richiede per abbinare una firma del metodo che è l'implicazione OOP più pura, ma che richiederebbe molti metodi aggiuntivi per gestire ciascuna interfaccia e potrebbe finire con più ramificazione complessa (if-else, switch-case).

    
risposta data 19.06.2015 - 20:28
fonte

Leggi altre domande sui tag