Questi principi seguono quanto indicato nellโ€™ottimo libro Head First Design Patterns (ISBN-13: 978-0596007126).

1. Separa le parti che cambiano da quelle che rimangono costanti

Durante il design di unโ€™applicazione, รจ necesaario essere in grado di individuare le parti del codice che variano (o potrebbero variare in futuro) ed incapsularle in modo che siano indipendenti da quelle che tendono a rimanere costanti. In questo modo, quando devo effettuare una modifica a tali parti, queste modifiche saranno molto piรน semplici e dovrรฒ andare a modificare solo il codice che effettivamente cambia.

2. Scegli la composizione invece dellโ€™ereditarietร 

Questa regola si puรฒ riassumere come: HAS-A รจ meglio di IS-A. Quando collego due classi insieme, inserendo una come attributo dellโ€™altra sto operando una composizione (HAS-A). Tipicamente fare in modo che un oggetto abbia una caratteristica esterna ad esso รจ meglio di creare รจ un oggetto intrinsicamente possiede tale caratteristica. Lโ€™esempio piรน semplice รจ la classe animale. Il comportamento dellโ€™animale puรฒ sia essere scritto come metodo della classe ma รจ una pratica di buona programmazione fare sรฌ che il comportamento sia un insieme di oggetti esterni allโ€™animale ma che viene posto in composizione con questo ultimo. Questo ha il vantaggio, oltre di rendere il codice piรน manutenibile, che posso cambiare il comportamento a runtime, rendono cosรฌ il codice estremamente flessibile.

3.Programma ad interfacce, non a implementazioni

In questo caso il concetto di interfaccia non significa solo lโ€™Interface di Java o C#, ma significa utilizzare il polimorfismo utilizzando superclassi (astratte o interfacce) al posto di classi concrete. Vediamo un esempio: Il seguente codice segue la programmazione per implementazione:

Dog d = new Dog();
d.bark();

Come si puรฒ notare questo codice รจ estremamente debole, in quanto se voglio cambiare animale dovrei cambiare entrambe le righe, e devo cambiarne una invece se voglio cambiare il verso. Questo perchรจ aver dichiarato d come Dog ci obbliga ad utilizzare solo i metodi della classe concreta Il codice seguente invece segue la programmazione a interfacce:

Animal a = new Dog();
a.makeSound();

in quanto io so che รจ un Dog, ma lo dichiaro con la sua superclasse astratta Animal che implementerร  il metodo astratto makeSound(). In questo modo qualora volessi cambiare animale, cambierei solo il nome dellโ€™istanza, facendo rimanere invece costante tutto il resto. Ancora meglio รจ astrarre ulteriormente evitando lโ€™istanziazione di una classe concreta hard-code, utilizzando un metodo che assegna lโ€™istanza della variabile a runtime.

Animal a = getAnimal();
a.makeSound();

in questo caso il grado di astrazione รจ massimo: noi vogliamo che sia un animale e che sappia emettere suoni, poi che animale sia e come fa ad emetterli non รจ un problema di questa classe.

4.Principio dellโ€™accoppiamento minimo

Due oggetti si dicono minimamente accoppiati quando sono in grado di interagire fra di loro pur conoscendo il minimo possibile della struttura reciproca. Questo significa che

  • sono flessibili allโ€™aggiunta o alla rimozione di oggetti correlati
  • per modificare un oggetto non devo modificare anche lโ€™oggetto a questo correlato
  • possiamo riusare un oggetto o lโ€™altro in maniera indipendente tra di loro

Strutturare il codice secondo questo principio ci permette di costruire sistemi OO flessibili che possono facilmente gestire il cambiamento reciproco.

5. Le classi devono essere aperte allโ€™estensione ma chiuse alla modifica

Un buon design deve creare delle classi che sono:

  • aperte allโ€™estensione: gli oggetti devono essere fatti in modo che sia il piรน facile estenderli al fine di incorporare una nuova funzionalitร 
  • chiusi alla modifica: una volta che un oggetto funziona correttamente senza bug, ogni sua modifica rischia di immetterne di nuovi. Un buon design deve fare in modo che le modifiche al codice esistente su una funzionalitร  vecchia siano ridotte al minimo

Il nostro obiettivo รจ qunidi quello di crare classi che siano facilmente estendibili al fine di incorporare un nuovo comportamento, senza che questo comportui la modifica del codice pre-esistente. Qualora vi sia la necessitร  di cambiare una funzionalitร  vecchia, il codice deve essere sufficentemente flessibile da poter incoporare la modifica con il minor numero possibile di modifiche, ma solo aggiunte. Attenzione perรฒ, usare questo principio dovunque potrebbe portare ad un aumento di complessitร  non necessario e a del codice difficile da comprendere. Scegliere con cura le parti gli oggettia cui applicare questo principio.

6. Dipendi dalle classi astratte, non concrete

Questo principio รจ anche detto il Dependency Inversion Principle e significa che i componenti ad alto livello non devono mai dipendere dai componenti a basso livello e nemmeno il contrario. Infatti entrambi devono dipendere da componenti astratti. Un componente si dice ad alto livello quando il suo comportamento รจ definito in termini di altri componenti a basso livello. Le seguenti linee guida aiutano a strutturare bene il codice secondo questo principio:

  • Nessuna variabile deve essere istanziata con lโ€™operatore new. Meglio usare una factory!
  • Le classi concrete non dovrebbero avere figli. Se derivo da una classe concreta, significa che dipendo da una classe concreta. Eโ€™ sempre meglio dipendere da astrazioni.
  • Non si dovrebbe mai eseguire un override di un metodo implementato dalla propria superclasse: se per poter eseguire il mio codice devo eseguire un override dalla superclasse, significa che questa non era unโ€™astrazione corretta. I metodi implementati nelle superclassi dovrebbero essere condivisi fra tutte le sottoclassi senza mai cambiare.

7. Principio della conoscenza minima

Il principio di minima conoscenza ci obbliga a sviluppare codice in cui gli oggetti comunicano solo con i loro โ€œamici viciniโ€. In poche parole, ogni oggetto deve comunicare con il minor numero possibile di oggetti diversi e quindi possiede la minima conoscenza possibile del sistema. Se cosรฌ non fosse avremmo molte classi tutte correlate fra di loro e il cambiamento di una porterebbe al cambiamento di molte altre. Lโ€™esempio classico di design errato sono metodi concatenati come il seguente:

a.getB().getC().getD()

Tipicamente ogni oggetto dovrebbe invocare solo metodi che appartengono a:

  • lโ€™oggetto stesso;
  • oggetti passati come parametro ad un metodo dellโ€™oggetto
  • oggetti creati o istanziati da un suo metodo
  • un componente dellโ€™oggetto (relazione HAS-A)

8. Il principio di Hollywood

Il principio di Hollywood si puรฒ riassumere con la seguente frase:

Non chiamarmi, ti chiamo io

Questo principio ci aiuta ad evitare lโ€™anello di dipendenze, per cui se una classe di alto livello dipende da un componenete di basso livello il quale dipende da uno di alto livello, ho una catena di componenti che rendono il sistema complesso da analizzare. Nel principio di Hollywood permettiamo ai componenti di basso livello di agganciarsi a dei componenti di alto livello, ma saranno loro a decidere quando questi sono necessari e come (che spiega la frase โ€œnon chiamarmi, ti chiamo ioโ€). Esempi di pattern che seguono questo principio sono il Factory, lโ€™Observer e il Template.

9.Principio di singola responsabilitร 

Un modo alternativo per definire questo principio รจ: una classe deve avere un solo motivo per cambiare. Il principio afferma che รจ necessario assegnare ad ogni classe una ed una sola responsabilitร , nel senso che si deve occupare solo di una cosa, unโ€™azione o un oggetto. In un sistema ben strutturato, al momento del cambiamento, ogni oggetto cambia per solo un aspetto, non di piรน.