In precedenza ho scritto una risposta su Open-Closed Principle (OCP) vs Liskov's Substitution Principle (LSP) ed entrambi questi principi si relazionano molto tra loro, ma sono ancora concettualmente diversi con alcuni esempi inventati di avere uno ma non l'altro. A causa di questa risposta, toccherò solo brevemente OCP e approfondirò il DIP e ciò che rende tale tick.
Proviamo a discutere su come OCP si relaziona e differisce con il principio di inversione di dipendenza (DIP) spiegando prima i diversi principi.
Principio di inversione di dipendenza
Leggendo i Principi di OOD di zio Bob scoprirai che DIP afferma quanto segue:
Dipende dalle astrazioni, non dalle concrezioni.
Un'astrazione in Java si ottiene semplicemente con interface
e abstract
parole chiave, il che significa che hai un "contratto" per alcune entità software che il codice deve seguire. Alcuni linguaggi di programmazione non hanno la possibilità di impostare esplicitamente comportamenti da seguire per il codice, quindi le astrazioni devono essere seguite in modo più manuale piuttosto che avere il compilatore che ti aiuti a far rispettare il contratto. Ad esempio in C ++ hai classi con metodi virtuali e linguaggi di programmazione dinamici come Javascript devi assicurarti di usare gli oggetti allo stesso modo (anche se nel caso di Javascript questo è stato esteso in TypeScript che aggiunge un sistema di tipi per aiutarti con la scrittura di contratti verificati dal compilatore).
Il nome include il termine "inversione" perché tradizionalmente (sai nei vecchi tempi bui della programmazione) scrivevi strutture software che avevano moduli di livello superiore a seconda dei moduli di basso livello. Ad esempio, aveva senso avere un ButtonAtKitchen
input di gestione per a KitchenLamp1
e KitchenLamp2
. Sfortunatamente ciò ha reso il software molto più specifico di quanto fosse necessario e il grafico a oggetti sarebbe simile a questo:
Quindi, quando rendi il software più generale, aggiungendo "contratti". Notare come le frecce nel grafico dell'oggetto "invertono" la direzione. Che le lampade da cucina ora dipendono da a Button
. In altre parole, i dettagli ora dipendono dalle astrazioni invece del contrario.
Quindi abbiamo una definizione più generale di DIP, dettagliata anche nell'articolo originale di DIP di zio Bob .
A. I moduli di alto livello non dovrebbero dipendere da moduli di basso livello. Entrambi dovrebbero dipendere dall'astrazione. B. Le astrazioni non dovrebbero dipendere dai dettagli. I dettagli dovrebbero dipendere dalle astrazioni.
Principio aperto-chiuso
Continuando dai principi di zio Bob scoprirai che OCP afferma quanto segue:
Dovresti essere in grado di estendere un comportamento delle classi, senza modificarlo.
Un esempio per raggiungere questo obiettivo è quello di utilizzare il modello di strategia in cui una Context
classe è chiusa per modifiche (cioè non è possibile cambiare il suo codice interno) ma è anche aperta per l'estensione attraverso le sue dipendenze collaborative (cioè le classi di strategia).
In senso più generale ogni modulo è costruito per essere estensibile attraverso i suoi punti di estensione.
OCP è simile a DIP, giusto?
No , non proprio.
Sebbene entrambi stiano discutendo delle astrazioni, sono concettualmente diversi. Entrambi i principi stanno esaminando contesti diversi, OCP su un modulo particolare e DIP su diversi moduli. Puoi ottenere entrambi allo stesso tempo come con la maggior parte dei modelli di design Gang of Four, ma puoi comunque allontanarti dal percorso.
Nell'esempio DIP sopra menzionato, con il pulsante e le lampade da cucina, nessuna delle lampade da cucina è estensibile (né attualmente ha alcun requisito che spiega che devono essere). Il design sta rompendo OCP ma segue DIP .
Un esempio inverso (e inventato) potrebbe essere una lampada da cucina estensibile (con il punto di estensione simile a una LampShade
) ma il pulsante dipende ancora dalle lampade . Sta rompendo DIP ma segue OCP .
Non ti preoccupare, succede
Questo è in realtà qualcosa che vedrai accadere spesso nel codice di produzione, che una parte di esso potrebbe violare un principio. Nei sistemi software più grandi (ovvero qualsiasi cosa più grande degli esempi sopra), potresti rompere un principio ma mantenerne l'altro di solito perché devi mantenere il codice semplice. Questo, a mio avviso, va bene per i moduli piccoli e autonomi, in quanto sono nel contesto correlato al principio di responsabilità singola (SRP).
Una volta che qualche modulo si complica anche se molto probabilmente bisogno di guardare ad essa con tutti i principi in mente e riprogettare o refactoring a qualche modello ben noto.