Quindi probabilmente, come molti, mi trovo spesso a dover affrontare mal di testa con problemi di progettazione in cui, ad esempio, esiste un modello / approccio di progettazione che sembra adattarsi intuitivamente al problema e ha i benefici desiderati. Molto spesso c'è qualche avvertenza che rende difficile implementare il modello / approccio senza un qualche tipo di lavoro attorno al quale poi si annulla il beneficio del modello / approccio. Posso facilmente finire iterando attraverso molti pattern / approcci, perché quasi tutti hanno delle avvertenze molto significative in situazioni reali in cui semplicemente non c'è una soluzione facile.
Esempio:
Ti darò un esempio ipotetico basato in modo approssimativo su uno reale che ho incontrato di recente. Diciamo che voglio usare la composizione sull'ereditarietà perché le gerarchie di ereditarietà hanno ostacolato la scalabilità del codice nel passato. Potrei rifattorizzare il codice ma poi scoprire che ci sono alcuni contesti in cui la superclasse / la classe di base semplicemente ha bisogno di chiamare funzionalità nella sottoclasse, nonostante i tentativi di evitarlo.
Il prossimo approccio migliore sembra implementare un modello di mezzo delegato / osservatore e un modello a metà composizione in modo che la superclasse possa delegare un comportamento o che la sottoclasse possa osservare eventi di superclasse. Quindi la classe è meno scalabile e mantenibile perché non è chiaro come dovrebbe essere estesa, inoltre è difficile estendere gli ascoltatori / delegati esistenti. Anche le informazioni non sono nascoste bene perché si inizia a dover conoscere l'implementazione per vedere come estendere la superclasse (a meno che non si utilizzino i commenti molto ampiamente).
Quindi, dopo questo, potrei scegliere semplicemente di usare semplicemente osservatori o delegati completamente per uscire dagli inconvenienti che derivano dal mescolare pesantemente gli approcci. Tuttavia questo ha i suoi problemi. Per esempio, potrei scoprire che alla fine ho bisogno di osservatori o delegati per una quantità crescente di comportamenti finché non avrò bisogno di osservatori / delegati per praticamente ogni comportamento. Un'opzione potrebbe essere quella di avere solo un grande ascoltatore / delegato per tutto il comportamento, ma poi la classe di implementazione finisce con molti metodi vuoti ecc.
Quindi potrei provare un altro approccio, ma ci sono altrettanti problemi con questo. Poi il prossimo, e quello dopo ecc.
Questo processo iterativo diventa molto difficile quando ogni approccio sembra avere tanti problemi quanti altri e porta a una sorta di paralisi della decisione progettuale . È anche difficile accettare che il codice finisca per essere ugualmente problematico, indipendentemente dal modello o dall'approccio di progettazione. Se finisco in questa situazione vuol dire che il problema stesso deve essere ripensato? Cosa fanno gli altri quando incontrano questa situazione?
Modifica Sembra esserci una serie di interpretazioni della domanda che voglio chiarire:
- Ho tolto completamente l'OOP perché risulta che in realtà non è specifico per OOP, inoltre è troppo facile interpretare erroneamente alcuni dei commenti che ho fatto durante il passaggio a OOP.
- Alcuni hanno affermato che dovrei adottare un approccio iterativo e provare diversi schemi, o che dovrei scartare un modello quando cessa di funzionare. Questo è il processo a cui intendevo fare riferimento in primo luogo. Ho pensato che questo fosse chiaro dall'esempio, ma avrei potuto renderlo più chiaro, quindi ho modificato la domanda per farlo.