Regola empirica per decidere a quale classe appartiene un metodo

6

Ad esempio, immagina un sito web che memorizza i risultati relativi a un determinato sport o gioco, che ha una tipica struttura "stagionale", in modo tale che sia sia una Player che una Season class. Per recuperare il punteggio medio di un giocatore durante una stagione specifica, dovrebbe essere fatto con $player->averageScore($season) , o dovrebbe essere fatto con $season->playerAverageScore($player) ? Perché?

    
posta Cal7 30.03.2016 - 18:24
fonte

8 risposte

9

Sembra quasi che tu abbia bisogno di una classe PlayerStatistics per calcolare le statistiche per un singolo giocatore. Potresti anche beneficiare di una classe SeasonStatistics per calcolare le statistiche per un'intera stagione. Al di sopra della mia testa, le statistiche vengono spesso mantenute per le seguenti cose:

  • Player
  • gioco
  • Team
  • Divisione
  • Serie di giochi (Baseball negli Stati Uniti ha una "serie" che è composta da due squadre che giocano più partite di fila)
  • Periodo di tempo (diciamo gli ultimi 3 anni per una squadra o un giocatore particolare)

Ciò disaccoppia in modo efficace questi calcoli dalle entità stesse (Giocatore, Stagione) e offre molte opzioni aggiuntive per il calcolo di tutti i tipi di statistiche a vari livelli.

Penso che il commento di Dunk sulla questione lo riassuma meglio e fornisce una linea guida molto buona per prendere decisioni in futuro:

... if it can legitimately go either way then you are missing a class that ties them together...

Non puoi essere più succinto di così.

    
risposta data 30.03.2016 - 18:38
fonte
3

Perché non una classe SeasonScoreCalculator con un metodo averageScore che tiene sia la stagione che il giocatore?

Essendo il punto, se non puoi decidere quale lato della recinzione dovrebbe cadere un metodo, allora quello potrebbe essere un segno che 1) le tue classi stanno facendo troppo e 2) ti manca un concetto che permetterebbe per semplificare.

    
risposta data 30.03.2016 - 18:37
fonte
2

Questi tipi di problemi sono generalmente risolti con un'analisi "chi ha bisogno di sapere chi".

Mi sembra che un giocatore debba sapere che cos'è una stagione, ma una stagione non dovrebbe sapere cos'è un giocatore. Pertanto, prenderei sicuramente l'approccio player->averageScore(season) .

Puoi utilizzare questo tipo di ragionamento in qualsiasi scenario, non solo quello a portata di mano.

    
risposta data 30.03.2016 - 21:06
fonte
0

La regola generale è guardare il modello di dati sottostante come sono rappresentati i punti. Non dichiari come i punti sono effettivamente rappresentati nel tuo modello in modo che nessuno possa rispondere per il tuo caso particolare, ma se immaginiamo un modello in cui un "punto" è un'entità che ha un riferimento a un "giocatore" e ad un "gioco" "(che a loro volta hanno un riferimento a una" stagione "), allora avresti naturalmente una classe Punti che ti permetterebbe di aggregare punti su giocatori e stagioni.

    
risposta data 01.04.2016 - 16:31
fonte
0

La regola da imparare e applicare è: -

"An action on a real-world object, becomes a method of that object in object-oriented code"

Un oggetto in codice orientato conosce se stesso come fare le cose. Quindi, un giocatore conosce i suoi punteggi e ha dei metodi per riflettere questo.

Per contrasto nel codice procedurale, una procedura chiede qualcos'altro (può essere un oggetto, un'altra procedura, dati, ecc.) per informazioni, quindi la procedura può determinare come e se agire.

    
risposta data 12.04.2016 - 09:33
fonte
0

Molto probabilmente il metodo appartiene alla stagione e non alla classe Player, ma potrebbe andare in entrambi i modi. La cosa più importante è prevenire le dipendenze circolari. Se Season chiama un qualsiasi metodo in Player, allora Player non dovrebbe chiamare alcun metodo in Season. E viceversa. Puoi scegliere quale dipende dall'altra. Se hai già metodi di Season che chiamano i metodi di Player allora non hai scelta - nessun metodo di Player può chiamare un metodo di Season.

    
risposta data 12.04.2016 - 09:50
fonte
0

Se questo è veramente per scopi statistici, come sottintende Greg Burghardt, penso che potresti impiegare esplicitamente i read-models , reso popolare in DDD. È una manifestazione del concetto CQRS . Il punto è che non hai bisogno di oggetti, usati da parte tua, implementando alcune logiche di business, per interrogare i dati. Quindi nel tuo caso potresti avere un repository separato, come il seguente:

class StatisticsRepository
{
    public function getByPlayerAndSeason(PlayerId $playerId, SeasonId $seasonId)
    {
        // db query
        return new Statistics(
            // here it goes raw data
        );
    }
}

Potresti avere i dati grezzi restituiti come un semplice array, oppure potresti avere oggetti di modello di lettura, che non hanno nulla a che fare con i tuoi oggetti di dominio avanzato usati dal tuo comando.

Ci sono un paio di profitti:

  • Semplicità. Non devi aderire ai tuoi oggetti comportamentali alla miriade di query. Altrimenti il tuo sito di comando si tufferà nella logica di interrogazione.
  • Quindi, molto probabilmente, performance. Hai a che fare con raw sql, non con ORM.
risposta data 04.12.2017 - 15:25
fonte
0

Probabilmente dovrebbe essere su una classe che non hai menzionato come getAverageScore(season, player) . Probabilmente, la stagione e il giocatore sono già membri della classe quindi potresti essere in grado di farla franca semplicemente getAverageScore() ...

Questa è una domanda molto vecchia in OOP perché il paradigma dà la priorità a un oggetto rispetto agli altri in un'interazione. Dopotutto, solo un oggetto può trovarsi a sinistra del nome del metodo mentre tutti gli altri devono trovarsi alla sua destra. (Se la mucca OO si disfa da sola o dovrebbe estrarre il latte dalla stessa vacca?)

La prima domanda da porsi è: quale oggetto deve cambiare stato come risultato della collaborazione? La classe di quell'oggetto è probabilmente quella che dovrebbe ospitare il metodo.

Ora probabilmente stai pensando che nessuno stato sta cambiando nel tuo esempio, ma probabilmente è sbagliato. Un pezzo di codice richiede il punteggio medio perché quel pezzo di codice sta pianificando facendo qualcosa con le informazioni e quindi è probabile che il metodo risieda.

    
risposta data 05.12.2017 - 02:34
fonte