Patterns should be used only where they can be the best solution or help in the creation of a good solution (do you agree?).
Vedo i modelli di progettazione rigorosamente come dettagli di implementazione. Se si documentano le API pubbliche e il programma su tale documentazione, in generale non importa (o si influisce molto su di voi) in cui si hanno schemi di progettazione. Cioè, tu non vai "Ho un modello di bridge qui, e attuerò un visitatore su di esso". Invece, "questa classe avrà implementazioni differenti su vari sistemi operativi, quindi verrà implementata usando un modello di bridge". Quindi, quando lo si utilizza, si è indifferenti al fatto che venga implementato come bridge - perché si guarda l'API pubblica, non un pattern di bridge.
how much effort should one actually invest in creating loosely coupled, flexible designs?
È possibile ottenere un accoppiamento lento seguendo una semplice serie di regole. Se li rispettate, il vostro codice sarà (più) liberamente accoppiato, come lo scrivete (cioè ogni sforzo è già parte del processo di sviluppo).
Tra le regole (non una lista esaustiva):
- definisce le tue interfacce pensando (o scrivendo) al codice client (come verrà usata la classe), non a quello che farà la classe (cioè, desing per l'interfaccia, non implementazione)
- "racconta, non chiedere"
- costruisci oggetti da parti già create
- passare nel costruttore gli oggetti reali che userai (non le fabbriche per i membri, i parametri per le fabbriche dei parametri, o qualcosa del genere).
- DRY (se hai due righe che appaiono nello stesso ordine in due punti, estraile in una funzione separata e così via).
- Se la creazione di un oggetto è un'operazione più complessa, implementa la creazione delle parti intermedie come metodo / classe di fabbrica (cioè non nel corpo del costruttore).
- YAGNI (crea le cose quando ne hai bisogno, non prima)
Queste regole sono seguite in modo diverso, a seconda della lingua, della metodologia di sviluppo seguita dal tuo team (ad es. TDD), dei vincoli di budget e così via.
Ad esempio, in Java, è buona norma definire la tua interfaccia come interface
e scrivere su tale codice client (quindi creare un'istanza dell'interfaccia con una classe di implementazione).
In C ++, d'altra parte, non hai interfacce, quindi puoi solo scrivere l'interfaccia come una classe base astratta; Poiché in C ++ si utilizza l'ereditarietà solo quando si ha un strong requisito per esso (e come tale si evita il sovraccarico di funzioni virtuali non necessarie), probabilmente non si definirà l'interfaccia separatamente, ma solo l'intestazione della classe).
Those who oppose design patterns say that the costs to using these patterns often outweighs the benefits.
Penso che stiano sbagliando. Se si scrive codice liberamente accoppiato (e ASCIUTTO), l'integrazione di schemi di progettazione in esso comporta uno sforzo minimo supplementare. Altrimenti, dovrai adattare il tuo codice per implementare un modello di design.
Se devi fare molte modifiche per implementare un modello di progettazione, il tuo problema è non il modello di progettazione - è il tuo codice base monolitico e strettamente accoppiato. Questo è un problema di progettazione non corretto / non ottimale, non un problema di progettazione.
What I'd like to know, is how much effort should I actually put in creating additional levels of abstraction and designs, only to allow my application to follow OO principles such as loose coupling, programmign to an interface, etc. Is it really worth it? How much effort should I put in this?
Le tue domande fanno presupposto (non dichiarato) che l'unico vantaggio di un accoppiamento libero è la capacità di implementare facilmente modelli di progettazione. Non lo è.
Tra i vantaggi di un accoppiamento libero ci sono:
- refactoring e riprogettazione della flessibilità
- meno sforzi inutili
- testability
- maggiore possibilità di riutilizzare il codice
- semplicità del design
- meno tempo trascorso nel debugger
... e alcuni altri che non mi vengono in mente in questo momento.