Le sottoclassi hanno solo metodi senza variabili

2

Va bene avere una sottoclasse che non dichiara le proprie variabili (variabili di istanza) ed eredita dalla classe super o dalla classe base? Ha dei metodi e ci sono alcuni metodi comuni che sovrasta ma non hanno variabili proprie.

Va bene avere sottoclassi che definiscono solo il comportamento o mi manca qualcosa? Voglio dividere in sottoclassi per separare la logica delle mie classi. Ho Medication class e voglio sottoclasse a DailyMedication , WeeklyMedication ma ho tutta la mia variabile di istanza in Medication in quanto non ho alcun specifico per queste classi. Voglio creare sottoclassi per dividere la logica in quanto ho troppe condizioni nel mio codice.

MedicationEntity
name
dose
weekdays    //in case of daily and monthly this is null 
monthdays   //in case of weekly and daily this is null
frequencyType  //Daily or weekly or monthly
etc

EDIT: Voglio creare sottoclassi perché tutte queste classi hanno una logica diversa. Ho enum di frequency (per Daily, Weekly etc) nella mia classe Medication e questo peggiora perché devo mettere tutti gli assegni nel mio codice per questo tipo di frequenza, come se la frequenza fosse quella di farlo e se ogni giorno lo faccio come adesso voglio rimuovere queste condizioni di commutazione.

1) Si prega inoltre di suggerire qualsiasi buona idea in quanto i giorni della settimana è nullo se la frequenza è giornaliera o mensile.Ma ho bisogno di inviare null al server.Così dove posso mettere i giorni feriali se sottoclasse in quanto non è nullo solo in caso di settimanale frequenza ma come devo inviare null al server in richiesta per tutte le altre frequenze. Dovrei mettere weekdays in sottoclasse se sì di come invio null al server se la frequenza è giornaliera in quanto DailyMedication non ha informazioni su weekdays .

    
posta codester 14.07.2014 - 22:24
fonte

4 risposte

1

Avere tre classi separate potrebbe avere senso se conosci aggiungerai alcuni membri speciali ad alcune di queste sottoclassi e non hai ancora trovato ancora em> (ma lo farai davvero presto, vero?).

Normalmente, sono d'accordo con lo sviluppatore senior: sembra una complicazione inutile. Potresti creare un'enumerazione chiamata Frequency e usarla per determinare quale tipo di Medication hai a che fare, qualcosa come questo pseudo-codice:

Enum MedicationFrequency{MONTHLY, DAILY, WEEKLY, HOURLY}

class Medication
{
    private MedicationFrequency frequency;
    ...
}

Suppongo che se si vuole veramente tenere separate le diverse implementazioni, si potrebbe provare ad iniettare algoritmi specifici nelle istanze Medication . Forse qualcosa del genere:

Interface MedicationProcess
{
   public void doSomething();
   public String getWhen();
   public MedicationFrequency getFreq();
}

class WeeklyMedProcess implements MedicationProcess
{
   String weekdays;
   public MedicationFrequency getFreqy() { return MedicationFrequency.WEEKLY;}
   public String getWhen() { return weekdays; }
   //...
}

class DailyMedProcess implements MedicationProcess
{
   public MedicationFrequency getFreqy() { return MedicationFrequency.DAILY;}
   //...
}

class MonthlyMedProcessimplements MedicationProcess
{
   String monthdays;
   public MedicationFrequency getFreqy() { return MedicationFrequency.MONTHLY;}
   public String getWhen() { return monthdays; }
   //...
}

class Medication
{
    private MedicationProcess someProcess;
    private String updateColumnName;

    public void setUpdateColumnName(String s)
    {
        this.updateColumnName= s;
    }

    public void setProcess(MedicationProcess p)
    {
        this.someProcess = p;
    }

    public doMedicationThing()
    {
        this.someProcess.doSomething();
    }
}

Facendo questo in Java, avresti una configurazione Spring che potrebbe assomigliare a questa:

<bean id="dailyMedProc" class="DailyMedProcess/>
<bean id="weeklyMedProc" class="WeeklyMedProcess/>

<bean id="medication1" class="Medication">
    <property name="process" ref="dailyMedProc/>
    <property name="updateColumnName" value="weekdays"/>
</bean>

<bean id="medication" class="Medication">
    <property name="process" ref="weeklyMedProc/>
    <property name="updateColumnName" value="monthdays"/>
</bean>

Per rispondere alla tua domanda su weekdays e monthdays , ci sono molti modi per farlo e dalla tua domanda non è chiaro quale sia weekdays e monthdays . Dato che li mostri come variabili singole, presumo che siano solo stringhe separate da virgole.

Sembra che tu abbia anche colonne in alcune tabelle denominate weekdays e monthdays che devono essere impostate su null se non c'è alcun valore. Dato che conosci già la frequenza del Medication (tramite il suo someProcess.getFreq ), sai quale campo impostare su null. Gli implementatori di MedicationProcess possono essere richiesti per implementare getWhen , che restituirà l'elenco separato da virgola di giorni / mesi-giorni. Con queste due informazioni, puoi facilmente aggiornare le colonne corrette.

Avere Medication ha una conoscenza su cosa fare quando si incontra uno specifico MedicationFrequencies interrompe un po 'il modello di progettazione. Non è grandioso, ma è una soluzione semplice.

Se non ti piace, potresti includere il nome della colonna che dovrebbe essere aggiornato per un'istanza specifica di Medication . Solo leggermente migliore - Non mi piace mescolare questo tipo di accesso ai dati di basso livello in tali classi, ma non ho visto il resto del tuo progetto, quindi forse è OK qui ...

Un'altra opzione potrebbe essere il fatto che MedicationProcess faccia il proprio inserimento / aggiornamento del database. Questo è un po 'più complicato e non posso davvero dare esempi concreti di come farlo senza conoscere i dettagli del tuo schema, anche se penso che questa sarebbe la soluzione migliore, se puoi implementarla.

Esistono altri modi per implementare il modello di strategia , a seconda degli strumenti e dei framework a tua disposizione.

Vedi anche: link La domanda è specifica per PHP , ma la risposta non lo è.

    
risposta data 14.07.2014 - 22:39
fonte
1

Posso vedere in entrambi i modi:

  • crea le sottoclassi: Se le sottoclassi hanno una logica veramente diversa, allora la logica dovrebbe essere separata anche se non ci sono variabili di istanza. Pensa al classico "ciao mondo" OOP: un programma di disegno con Shape e diverse sottoclassi per Square e Triangle . CalculateArea() sarebbe diverso, ma entrambi funzionerebbero in altezza e larghezza.
  • tenerli insieme: nel tuo esempio, potrei avere una classe Medication con una proprietà che rappresenta l'idea di un intervallo o di una durata. In un mondo ideale avresti solo una serie di logiche che funzionerebbero su un "Intervallo" senza preoccuparti molto se fosse ogni 1 ora, ogni 24 o ogni 168.

Senza ulteriori informazioni il mio sospetto è che il tuo senior dev sia corretto per la tua situazione particolare. (Questo è il modo in cui gli sviluppatori senior diventano supervisori senior - avendo ragione più spesso di quanto non abbiano torto)

Penso che tu abbia ragione a voler rimuovere tutto quel codice condizionale. La domanda è se sia meglio nascondere le condizioni in sottoclassi o ripensare il processo in modo che l'intervallo diventi semplicemente un parametro.

Guarda in un altro modo: che cosa dovresti fare se il tuo capo ti chiedesse di sostenere un nuovo periodo di tempo (giornaliero, settimanale, mensile, ogni 17 ore, ogni 3 giorni, ecc.)? Puoi codificare i tuoi metodi in modo che invece di guardare le enumerazioni puoi lavorare sull'intervallo?

Potrebbe essere necessario parlare con lo sviluppatore senior & guarda cosa ha in mente. È possibile che la sua preoccupazione si basi su una conoscenza insufficiente dei problemi.

    
risposta data 14.07.2014 - 22:44
fonte
0

A volte ha senso avere una gerarchia di classi in cui le classi derivate differiscono solo nei metodi di implementazione dalle classi base. In particolare, se si dispone di una gerarchia di oggetti che hanno gli stessi input, gli stessi output e interagiscono in modo identico con il resto del sistema, ma comunque hanno una logica di implementazione sostanzialmente diversa, è possibile considerare l'utilizzo di una gerarchia metodo-only.

Ad esempio, considera il software CAD che consente agli utenti di aggiungere oggetti di "misura" al proprio database. Ogni entità di misura mantiene un riferimento persistente a una o più entità geometriche e genera un numero reale (la misurazione) in un sistema di unità specificato. Ora, potrebbero esserci molti diversi algoritmi di misurazione: lunghezza dell'arco, raggio massimo / minimo di curvatura o extrema lungo un vettore di curve selezionate; superficie o perimetro delle facce selezionate; e così via. È probabile che ogni versione del sistema CAD aggiunga nuovi tipi di misurazione. Ma dovrebbero comportarsi tutti allo stesso modo del resto del sistema. Per esempio. se le entità geometriche vengono copiate e incollate, le loro misure dovrebbero essere copiate insieme ai riferimenti aggiornati. O se un'entità geometrica è evidenziata, forse le sue misure dovrebbero essere interrogate dal database e mostrate in una finestra. Finché esiste un codice identico sostanziale che riguarda gli aspetti generici delle entità di misura, una gerarchia di classi solo metodo (probabilmente il mirroring di una gerarchia di interfacce) è un'implementazione ragionevole.

Nel tuo caso, tuttavia, stai creando una classe di livello superiore che è l'unione di altre classi con input o output sostanzialmente diversi, ad esempio:

public abstract class Top
{
    public abstract double? Sub1Properties { get; }

    public abstract double? Sub2Properties { get; }
}

public class Sub1 : Top 
{
    double data = 24.0 // or whatever.
    public override double? Sub1Properties
    {
        get
        {
            return data;
        }
    }
    public override double? Sub2Properties
    {
        get
        {
            return null;
        }
    }
}

Questo, penso sia sfortunato. I consumatori delle tue classi probabilmente scriveranno il codice in questo modo:

        if (top.Sub1Properties != null)
        {
            // DO something
        }
        else if (top.Sub2Properties != null)
        {
            // Do something
        }

Ora immagina che, domani, devi aggiungere una terza sottoclasse:

public abstract class Top
{
    public abstract double? Sub1Properties { get; set; }

    public abstract double? Sub2Properties { get; set; }

    public abstract double? Sub3Properties { get; set; }
}

Tutte le dichiarazioni di if dovranno essere aggiornate. Questa è una logica procedurale obsoleta, ed è IMHO un anti-modello. Nel tuo caso, se hai bisogno di aggiungere un nuovo regime di dosaggio (bimestrale) in una versione successiva del software tutte quelle istruzioni if dovrebbero essere aggiornate.

Nel tuo caso, potrei suggerire di dividere "Medication" e "Dosing Schedule" in oggetti / tabelle separati, con ogni farmaco che ha un link alla sua pianificazione appropriata. Quindi aggiungere un nuovo regime richiederebbe solo una nuova voce nella tabella. Potrei anche rappresentare le frequenze in modo generico verso il mondo esterno, ad esempio utilizzando un TimeSpan . Se esiste una logica sostanziale specifica per ciascun regime, potrebbe essere rappresentata come una gerarchia di classi, purché esista una rappresentazione generica del mondo esterno per tutti i dati in esso contenuti.

    
risposta data 15.07.2014 - 06:06
fonte
0

Take 2.

Capisco che tu abbia i seguenti vincoli:

  1. Non è possibile modificare il modello di dati sottostante sul server affatto . La rappresentazione lato server di "MedicationEntity" ha campi codificati che corrispondono a un numero limitato di possibili insiemi di schemi di dosaggio e devi essere in grado di tradurre da & a questo modello.

  2. I vari schemi di dosaggio non possono essere generalizzati come un insieme di intervalli di tempo standard. Per esempio. "M, Th, S" potrebbe essere un programma di dosaggio settimanale valido. Questa mancanza di perfetta uniformità uccide l'idea di rappresentare tutte le frequenze come un semplice TimeSpan .

In questo caso, suggerirei che la tua classe "MedicationEntity" dovrebbe nascondere completamente i campi della frequenza di dosaggio lato server. Deve necessariamente ricordarli internamente, poiché è ciò che è memorizzato sul server, ma non dovrebbe mai presentarli come proprietà pubbliche. Invece dovrebbe avere qualche proprietà "DosageFrequency" che restituisce un'istanza di una classe che implementa un'interfaccia "IDosageFrequency" (qui sto usando c # perché mi è più familiare):

public interface IMedicationEntity
{
    string Name { get; }

    double Dose { get; }

    string Units { get; } // 

    IDosageFrequency DosageFrequency { get; set; }
}

L'interfaccia "IDosageFrequency" potrebbe incapsulare tutta la logica aziendale per ogni frequenza di dosaggio e presentarli in modo neutro, ad esempio:

public interface IDosageFrequency
{
    string Name { get; }

    string Description { get; } // Localized for presentation in the user interface.

    string FormattedPromptString(IMedicationEntity medication); // E.g.: string.Format("{0} {1} of {2} three times a week.", med.Dose, med.Units, med.Name)

    DateTime NextDoseTime(DateTime now);

    TimeSpan DoseTimeRequiredAccuracy { get; }

    IEnumerable<DateTime> DoseTimesBetween(DateTime start, DateTime end);

    // etc
}

Potresti quindi implementare questa interfaccia con classi diverse corrispondenti a ciascun schema di dosaggio di base. È inoltre possibile utilizzare il modello di fabbrica astratto per conservare le librerie di oggetti standard DosageFrequency da presentare nell'interfaccia utente, per consentire agli utenti finali di selezionare un nuovo schema di dosaggio per alcuni farmaci preesistenti o nuovi.

Questo ti lascerebbe con solo due brutte istruzioni switch, entrambe nella tua classe "MedicationEntity":

  1. Costruire la frequenza IDosage appropriata dai campi memorizzati dal server, AND

  2. Selezione dei campi server appropriati quando viene impostato un nuovo oggetto IDosageFrequency.

Nascondendo gli schemi di dosaggio hardcoded di livello sever, questo modello (c'è un nome per questo?) potrebbe impedire che le dichiarazioni switch aumentino senza controllo, evitando anche la necessità di creare diversi tipi di classi di farmaci per diversi schemi di dosaggio.

Aggiornamento: per inciso, ho appena notato una potenziale trappola con il tuo schema di creare una gerarchia di classi per "MedicationEntity: objects." Se hai bisogno di implementare la possibilità di cambiare il dosaggio schema per un pre -esistente MedicationEntity, dovrai immediatamente sostituire localmente il MedicationEntity esistente con un'istanza di una sottoclasse diversa, ad esempio DailyMedicationEntity - > WeeklyMedicationEntity; in caso contrario, non sarebbe possibile lavorare localmente con le nuove impostazioni. la rappresentazione di MedicationEntity non dovrebbe essere eliminata e ricreata, il che significa che temporaneamente potresti avere istanze di MedicationEntity sostituite nel tuo client che si riferiscono a entità di database ancora esistenti. Questo, temo, potrebbe causare problemi.

    
risposta data 15.07.2014 - 09:17
fonte

Leggi altre domande sui tag