L'idea alla base di OOP è che i dati e il comportamento (su quei dati) sono inseparabili e sono accoppiati all'idea di un oggetto di una classe. L'oggetto ha dati e metodi che funzionano con quello (e altri dati). Ovviamente secondo i principi di OOP, gli oggetti che sono solo dati (come le strutture C) sono considerati un anti-pattern.
Fin qui tutto bene.
Il problema è che ho notato che il mio codice sembra andare sempre più nella direzione di questo anti-pattern ultimamente. Mi sembra che più cerco di ottenere informazioni nascoste tra classi e progetti vagamente accoppiati, più le mie classi diventano un mix di dati puri senza classi di comportamento e tutto il comportamento senza classi di dati.
In genere progetto classi in modo da ridurre al minimo la consapevolezza dell'esistenza di altre classi e minimizzare la conoscenza delle interfacce di altre classi. In particolare, lo impongo dall'alto in basso, le classi di livello inferiore non conoscono le classi di livello superiore. Per esempio:
Supponiamo di avere un'API di gioco di carte generale. Hai una lezione Card
. Ora questa Card
classe deve determinare la visibilità per i giocatori.
Un modo è quello di avere boolean isVisible(Player p)
in Card
classe.
Un altro è quello di avere boolean isVisible(Card c)
in Player
classe.
Non mi piace il primo approccio in particolare poiché garantisce la conoscenza di una Player
classe di livello superiore a una Card
classe di livello inferiore .
Invece ho optato per la terza opzione in cui abbiamo una Viewport
classe che, dato un Player
e un elenco di carte determina quali carte sono visibili.
Tuttavia questo approccio ruba entrambi Card
e Player
classi di una possibile funzione membro. Una volta che lo fai per cose diverse dalla visibilità delle carte, ti rimangono Card
e le Player
classi che contengono dati puramente poiché tutte le funzionalità sono implementate in altre classi, che sono per lo più classi senza dati, solo metodi, come Viewport
sopra.
Ciò è chiaramente contrario all'idea principale di OOP.
Qual è il modo corretto? Come dovrei svolgere il compito di ridurre al minimo le interdipendenze tra le classi e minimizzare le conoscenze e l'accoppiamento presunti, ma senza concludere con uno strano design in cui tutte le classi di basso livello contengono solo dati e le classi di alto livello contengono tutti i metodi? Qualcuno ha una terza soluzione o prospettiva sul design di classe che evita l'intero problema?
PS Ecco un altro esempio:
Supponiamo che tu abbia una classe DocumentId
immutabile, che abbia un solo BigDecimal id
membro e un getter per questo membro. Ora è necessario disporre di un metodo da qualche parte, che ha DocumentId
restituito Document
questo ID da un database.
Fai:
- Aggiungi il
Document getDocument(SqlSession)
metodo allaDocumentId
classe, introducendo improvvisamente la conoscenza della tua persistenza ("we're using a database and this query is used to retrieve document by id"
), l'API utilizzata per accedere a DB e simili. Anche questa classe ora richiede il file JAR di persistenza solo per la compilazione. - Aggiungi un'altra classe con il metodo
Document getDocument(DocumentId id)
, lasciando laDocumentId
classe come morta, nessun comportamento, classe simile a una struttura.