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-modello.
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 che nascondono tra classi e disegni liberamente accoppiati, più le mie classi diventano un mix di dati puri, nessuna classe di comportamento e tutti i comportamenti non classi di dati.
Generalmente disegno le classi in un modo che minimizza la loro consapevolezza dell'esistenza di altre classi e riduce al minimo la loro conoscenza delle interfacce di altre classi. In particolare, lo impongo in modo top-down, le classi di livello inferiore non conoscono le classi di livello superiore. Per esempio:.
Supponiamo che tu abbia un'API generale di giochi di carte. Hai una classe Card . Ora questa classe Card deve determinare la visibilità dei giocatori.
Un modo è avere boolean isVisible(Player p) su Card class.
Un altro è avere boolean isVisible(Card c) su Player class.
Non mi piace il primo approccio in particolare in quanto garantisce la conoscenza della classe di livello superiore Player a una classe di Card di livello inferiore.
Ho optato invece per la terza opzione in cui abbiamo una classe Viewport che, dato un Player e un elenco di carte determina quali carte sono visibili.
Tuttavia questo approccio ruba entrambe le classi Card e Player di una possibile funzione membro. Una volta fatto questo per altre cose oltre alla visibilità delle carte, rimani con Card e Player classi che contengono puramente dati poiché tutte le funzionalità sono implementate in altre classi, che sono per lo più classi senza dati, solo metodi, come il Viewport sopra.
Questo è chiaramente contro l'idea principale di OOP.
Qual è il modo corretto? Come dovrei andare sul compito di ridurre al minimo le interdipendenze di classe e ridurre al minimo la conoscenza e l'accoppiamento presunte, ma senza finire con il design strano 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 una prospettiva sulla progettazione della classe che evita l'intero problema?
P.S. Ecco un altro esempio:
Supponiamo di avere classe DocumentId che è immutabile, ha solo un membro BigDecimal id e un getter per questo membro. Ora devi avere un metodo da qualche parte, che ha dato un DocumentId restituisce Document per questo ID da un database.
Fai:
- Aggiungi il metodo
Document getDocument(SqlSession)alla classeDocumentId, introducendo improvvisamente conoscenza sulla 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 metodo
Document getDocument(DocumentId id), lasciandoDocumentIdclass come dead, no behavior, struct-like class.