In breve, ogni volta che usi "nuovo", stai strettamente accoppiando la classe che contiene questo codice all'oggetto che si sta creando; per istanziare uno di questi oggetti, la classe che fa l'istanziazione deve sapere della istanza della classe concreta. Quindi, quando usi "nuovo", dovresti considerare se la classe in cui stai ponendo l'istanziazione è un posto "buono" in cui risiede la conoscenza, e sei disposto a fare cambiamenti in quest'area se la forma del l'oggetto istanziato doveva cambiare.
L'accoppiamento stretto, cioè un oggetto che ha conoscenza di un'altra classe concreta, non è sempre da evitare; a un certo livello, qualcosa, ALCUNE PARTI, deve sapere come creare questo oggetto, anche se tutto il resto riguarda l'oggetto perché ne viene fornita una copia da qualche altra parte. Tuttavia, quando la classe in fase di creazione cambia, qualsiasi classe che conosce l'implementazione concreta di quella classe deve essere aggiornata per gestire correttamente le modifiche di quella classe.
La domanda che dovresti sempre porre è: "Avere questa classe sapere come creare quest'altra classe diventerà una responsabilità quando manterrai l'app?" Entrambe le principali metodologie di progettazione (SOLID e GRASP) di solito rispondono "sì", per ragioni leggermente diverse. Tuttavia, sono solo metodologie ed entrambi hanno il limite estremo che non sono stati formulati in base alla conoscenza del tuo programma unico. In quanto tali, possono solo commettere errori di prudenza e presumere che qualsiasi punto di accoppiamento stretto causerà EVENTUALMENTE un problema relativo all'effetto di apportare modifiche a uno o entrambi i lati di questo punto. Devi prendere una decisione definitiva conoscendo tre cose; la migliore pratica teorica (che è quella di accoppiare liberamente tutto perché tutto può cambiare); il costo di implementazione della best practice teorica (che può includere diversi nuovi livelli di astrazione che faciliteranno un tipo di cambiamento mentre ne ostacoleranno un altro); e la probabilità del mondo reale che il tipo di cambiamento che stai anticipando sarà sempre necessario.
Alcune linee guida generali:
-
Evita l'accoppiamento stretto tra le librerie di codice compilate. L'interfaccia tra DLL (o un EXE e le sue DLL) è il luogo principale in cui l'accoppiamento stretto presenterà uno svantaggio. Se si apporta una modifica a una classe A nella DLL X e la classe B nel file EXE principale conosce la classe A, è necessario ricompilare e rilasciare entrambi i file binari. All'interno di un singolo binario, l'accoppiamento più stretto è generalmente più permissibile perché l'intero binario deve essere ricostruito per qualsiasi cambiamento comunque. A volte, dover ricostruire più file binari è inevitabile, ma è necessario strutturare il codice in modo da poterlo evitare laddove possibile, soprattutto per le situazioni in cui la larghezza di banda è scarsa (ad esempio la distribuzione di app mobili, la sostituzione di una nuova DLL in un aggiornamento è molto più economica di spingere l'intero programma).
-
Evita l'accoppiamento stretto tra i principali "centri logici" del tuo programma. Puoi pensare a un programma ben strutturato come composto da sezioni orizzontali e verticali. Le sezioni orizzontali possono essere i livelli di applicazione tradizionali, come interfaccia utente, controller, dominio, DAO, dati; le sezioni verticali potrebbero essere definite per singole finestre o viste o per singole "user story" (come la creazione di un nuovo record di un tipo di base). Quando si effettua una chiamata che si sposta verso l'alto, il basso, sinistra o destra in un sistema ben strutturato, si dovrebbe in genere astrarre detta chiamata. Ad esempio, quando la convalida deve recuperare i dati, non dovrebbe avere accesso diretto al DB, ma dovrebbe effettuare una chiamata a un'interfaccia per il recupero dei dati, che è supportata dall'oggetto reale che sa come farlo. Quando alcuni controlli dell'interfaccia utente devono eseguire una logica avanzata che coinvolga un'altra finestra, dovrebbero astrarre l'attivazione di questa logica tramite un evento e / o un callback; non deve sapere cosa verrà fatto di conseguenza, permettendoti di cambiare ciò che verrà fatto senza cambiare il controllo che lo innesca.
-
In ogni caso, considera quanto un cambiamento sarà facile o difficile e quanto probabile sarà il cambiamento. Se un oggetto che stai creando viene sempre e solo usato da un posto, e non prevedi che cambi, l'accoppiamento stretto è generalmente più ammissibile, e potrebbe anche essere superiore in questa situazione per perdere l'accoppiamento. L'accoppiamento lento richiede l'astrazione, che è un livello aggiuntivo che impedisce il passaggio agli oggetti dipendenti quando l'implementazione di una dipendenza deve cambiare. Tuttavia, se l'interfaccia stessa deve cambiare (aggiungendo una nuova chiamata di metodo o aggiungendo un parametro a una chiamata di metodo esistente), un'interfaccia effettivamente aumenta la quantità di lavoro necessaria per apportare la modifica. Devi valutare la probabilità che diversi tipi di cambiamenti abbiano un impatto sul design e che sia impossibile o impossibile realizzare un sistema che è chiuso a tutti i tipi di cambiamento.