Proprietà calcolate in OOP

-1

Esiste una convenzione su come dichiarare, denominare, utilizzare o accedere alle proprietà degli oggetti che non aggiungono nuove informazioni e potrebbero essere ricalcolati in qualsiasi momento? Esempio di base (creato questo, solo per chiarimenti):

class User {
    // provided by DB
    int ID;
    // calculated
    boolean id_is_odd;

    // provided by DB
    Timestamp[] visits;
    // calculated
    long shortest_time_between_visits;
}

Dal momento che controllo un intero per stabilire se è pari o dispari, implementerei id_is_odd() come funzione membro.

Ma forse visits è molto grande e si dovrebbe evitare di calcolare shortest_time_between_visits più volte. Quindi cosa fare con esso? Devo fare un getter che controlla se private shortest_time_between_visits è nullo e, in caso affermativo, calcola prima? Sarebbe anche chiamato "getter" quando stiamo recuperando solo un valore calcolato? O definendo in costruttore? Esistono anche degli standard che definiscono una differenza tra proprietà native e calcolate? Non sono riuscito a trovare alcuna informazione su questo.

Ho taggato java, ma questo problema può essere applicato a più lingue con supporto per oggetti come Javascript.

    
posta Blauhirn 03.12.2017 - 04:22
fonte

4 risposte

2

Ci sono diversi dubbi sulla tua domanda, proverò ad affrontarne alcuni.

Is there any convention on how to declare, name, use or access object properties that themselves do not add new information and could be re-calculated at any point in time?

Ogni oggetto ha identità, stato e comportamento. L'identità è ciò che rende un oggetto diverso dagli altri oggetti, lo stato è ciò che un oggetto conosce di sé, la sua conoscenza autoincapsulata e il comportamento è ciò che i servizi possono fornire agli altri. Quindi nel tuo primo esempio, come hai detto tu, la conoscenza dell'id oddness non si aggiunge allo stato dell'oggetto, quindi sicuramente dovrebbe essere il suo comportamento. Ma per mantenere pulita la classe User e seguire la politica che i dati e il comportamento dovrebbero risiedere insieme, creerei una classe UserId e inserirò il metodo isOdd . Se utilizzi DDD , è prassi diffusa diffondere le proprie classi per gli ID di entità.

But maybe visits is very big and one should avoid calculating shortest_time_between_visits multiple times. So what to do with it?

Ci sono diverse opzioni qui. Innanzitutto, nulla ti impedisce di farlo in un database. Di conseguenza, ottieni un oggetto Visits con tutti i dati richiesti già calcolati. Ha senso se è davvero molto grande, come dici tu. I database sono bravi.

Se sei pronto a calcolarlo nell'applicazione stessa, esiste una tecnica chiamata Memoization . Fondamentalmente, è una cache semplice. Ogni volta che vengono chiamate le funzioni, controlla se il risultato per gli argomenti dati è già stato calcolato. Se lo era, il risultato è restituito. In caso contrario, viene calcolato e inserito nella cache. Ma di nuovo, farei sicuramente una classe separata per quello. E qui è una soluzione generica per java 8.

Esistono anche lingue con supporto nativo per questo tipo di problemi. La tecnica che usano è chiamata valutazione pigra . Inoltre le espressioni vengono valutate solo nel momento in cui vengono effettivamente utilizzate, vengono valutate solo una volta.

Would that even be called a "getter" when we are retrieving only a calculated value?

Per mantenere incapsulati gli oggetti, nessuno dovrebbe sapere se si recupera una proprietà o un valore calcolato. Quindi i tuoi metodi dovrebbero avere lo stesso aspetto e seguire la stessa politica di denominazione. Si chiama il principio di accesso uniforme .

    
risposta data 03.12.2017 - 10:04
fonte
5

Suggerirei che ti stai avvicinando alla domanda sbagliata. I getter sono solo una specie di funzione membro (mentre la leggo, si pensa a queste come funzioni speciali che si associano abbastanza direttamente all'archiviazione di backup). Ma questo è precisamente il tipo di conoscenza interna che OOP dovrebbe nascondere all'utente del tuo oggetto e dall'esterno tutti i "punti di accesso" dovrebbero apparire uguali.

L'importante è assicurarti di mantenere uno stato coerente nell'oggetto, di solito il più semplice ottenuto non memorizzando più dati del necessario e calcola quanti più risultati possibili. (In senso stretto un semplice getter è anche un valore calcolato, il più semplice, concettualmente 1 * backing store).

Considera anche che nella maggior parte delle lingue ci sono zucchero sintattico per generare facilmente coppie setter / getter di base, ma potresti anche scriverle completamente e quindi puoi vedere più facilmente che non c'è davvero alcuna differenza concettuale tra queste e le altre funzioni dei membri. (In effetti tendo a farlo solo perché ho trovato che è molto raro che ho bisogno solo del getter più semplice e alla fine dovrò adattarlo per aggiungere più funzioni ad esso)

Inoltre, l'idea è che dovresti essere in grado di modificare gli interni senza che nessuno se ne accorga. Quindi, se inizi con un elemento di dati memorizzati e calcoli le varie rappresentazioni da quello che suggerisci, puoi testare e vedere se ci sono problemi di prestazioni. Se lo si cambia, forse è necessario disporre di un'altra rappresentazione interna e modificare il getter più semplice dei dati originali in un calcolo più complesso o forse, come dici di trovare alcuni problemi con tempi di calcolo lunghi, nel qual caso puoi migliorarlo da una cache interna, ma non aggiungere prematuramente complessità. Se lo fai, in genere hai bisogno di una funzionalità aggiuntiva in setter per eliminare la cache quando cambi i dati di backup che mi riporta al punto precedente sulla necessità di avere setter / getter più avanzati di quelli generati automaticamente.

    
risposta data 03.12.2017 - 08:32
fonte
2

Il principio di accesso uniforme ci dice che:

All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.

Ciò significa che, al di fuori della classe, dovrebbe essere impossibile stabilire se le informazioni vengano restituite semplicemente facendo riferimento a una variabile o eseguendo un calcolo.

Detto questo, può esserci un valore per il chiamante per conoscere la complessità del servizio tempo . Il tuo id_is_odd getter ha una complessità temporale di O (1) proprio come ID mentre shortest_time_between_visits ha una complessità di O (n), inoltre potrebbe generare un'eccezione o restituire un valore sentinella. Per questi motivi, si può fare un buon argomento per esporre questo servizio in modo diverso rispetto agli altri due.

Vorrei rendere shortest_time_between_visits una funzione.

    
risposta data 03.12.2017 - 12:35
fonte
0

C'è un buon post del blog che descrive approcci diversi a questo argomento e quando utilizzarli. Puoi scegliere il miglior approccio in base alle tue esigenze.

    
risposta data 21.04.2018 - 07:43
fonte

Leggi altre domande sui tag