@@ -5,11 +5,11 @@ Prima di proseguire è bene richiamare concetti e termini fondamentali presumibi
...
@@ -5,11 +5,11 @@ Prima di proseguire è bene richiamare concetti e termini fondamentali presumibi
## Object orientation
## Object orientation
Per essere definito _object oriented_, un linguaggio di programmazione deve soddisfare tre proprietà:
Per essere definito _object oriented_, un linguaggio di programmazione deve soddisfare tre proprietà:
- __ereditarietà__: ovvero la possibilità di poter definire una classe ereditando proprietà e comportamenti di un'altra classe.
- __Ereditarietà__: ovvero la possibilità di poter definire una classe ereditando proprietà e comportamenti di un'altra classe.
- __polimorfismo__: quando una classe può assumere diverse forme in base alle interfacce che implementa.
- __Polimorfismo__: quando una classe può assumere diverse forme in base alle interfacce che implementa.
Il prof fa l'esempio del _tennista scacchista_: in un torneo di tennis è poco utile sostituire una persona che gioca a tennis ed è brava con gli scacchi (quindi una classe che implementa entrambe le interfacce) con una che gioca a scacchi.
Il prof fa l'esempio del _tennista scacchista_: in un torneo di tennis è poco utile sostituire una persona che gioca a tennis ed è brava con gli scacchi (quindi una classe che implementa entrambe le interfacce) con una che gioca a sia a tennis che a scacchi, basta che sappia giocare tennis.
Il collegamento tra capacità e oggetto è fatto __a tempo di compilazione__: non è importante quindi se la capacità non è ancora definita;
Il collegamento tra capacità e oggetto è fatto __a tempo di compilazione__: non è importante quindi se la capacità non è ancora definita;
- __collegamento dinamico__: in Java il tipo concreto degli oggetti e quindi il problema di stabilire _quale metodo chiamare_ viene risolto durante l'esecuzione.
- __Collegamento dinamico__: in Java il tipo concreto degli oggetti può non essere specificato staticamente e quindi il problema di stabilire _quale metodo chiamare_ viene risolto durante l'esecuzione.
In C++ occorre esplicitare questo comportamento utilizzando la keyword `virtual`.
In C++ occorre esplicitare questo comportamento utilizzando la keyword `virtual`.
@@ -33,13 +33,14 @@ Meglio quindi avere __tante interfacce specifiche__ e piccole (composte da pochi
...
@@ -33,13 +33,14 @@ Meglio quindi avere __tante interfacce specifiche__ e piccole (composte da pochi
il codice dal quale una classe dipende non deve essere più __concreto__ di tale classe.
il codice dal quale una classe dipende non deve essere più __concreto__ di tale classe.
Per esempio, se il _telaio della FIAT 500_ dipende da uno specifico motore, è possibile utilizzarlo solo per quel specifico motore.
Per esempio, se il _telaio della FIAT 500_ dipende da uno specifico motore, è possibile utilizzarlo solo per quel specifico motore.
Se invece il telaio dipende da _un_ concetto di motore, non c'è questa limitazione.
Se invece il telaio dipende da _un_ concetto di motore, non c'è questa limitazione.
In conlusione, le classi concrete devono tendenzialmente dipendere da classi astratte e non da altre classi concrete.
In conclusione, le classi concrete devono tendenzialmente dipendere da classi astratte e non da altre classi concrete.
## Reference escaping
## Reference escaping
Il _reference escaping_ è una violazione dell'incapsulamento.
Il _reference escaping_ è una violazione dell'incapsulamento, __compiere questo errore equivale ad una bocciatura diretta all'esame.__
Può capitare, per esempio quando stiamo gestendo un mazzo di carte che vogliamo sia __segreto__, quindi non rivelare la sua implementazione o composizione, ecco i possibili _errori_:
Può capitare, per esempio:
- quando un getter ritorna un riferimento a un segreto;
- quando un getter ritorna un riferimento a un segreto;
```java
```java
publicDeck{
publicDeck{
...
@@ -79,32 +80,38 @@ __Legge di Parnas (L8)__.
...
@@ -79,32 +80,38 @@ __Legge di Parnas (L8)__.
Lo stato mostrato all'esterno non può essere modificato, mentre quello nascosto sì.
Lo stato mostrato all'esterno non può essere modificato, mentre quello nascosto sì.
Questo principio serve per __facilitare la comprensione del codice__ e renderne più facile la modifica parziale senza fare danni.
Questo principio serve per __facilitare la comprensione del codice__ e renderne più facile la modifica parziale senza fare danni. Dovrà essere quindi chiarito prima dell'implementazione ciò che sarà pubblico e ciò che invece sarà privato.
## Legacy o Deprecated
Una classe o una funzionalità, dopo diverse modifiche nel tempo, possono arrivare un punto di non ritorno, dove l'evoluzione si ferma per diversi motivi, come un design iniziale troppo limitante o l'arrivo di un innovazione tecnologica. In questo caso può avere due possibili fini:
- __Legacy__: Una classe di questo genere continuerà a funzionare e sarà supportata, però verrà consigliato l'utilizzo di un altra classa più recente.
- __Deprecated__: In questo caso la classe resterà comunque funzionante ma non sarà più supportata. Il suo utilizzo sarà fortemente sconsigliato e si spingerà verso la sua sostituzione con classi più recenti quando già utilizzata poichè dopo un certo lasso di tempo verrà rimossa o la sua funzionalità non sarà più garantita.
## Immutabilità
## Immutabilità
Una classe è immutabile quando non c'è modo di modificare lo stato di ogni suo oggetto dopo la creazione.
Una classe è immutabile quando non c'è modo di modificare lo stato di ogni suo oggetto dopo la creazione. Questo ci permette grandi vantaggi e quindi sarà fondamentale _massimizzare_ le classi con questa caratteristica
Per assicurare tale proprietà è necessario:
Per assicurare tale proprietà è necessario:
- __non fornire metodi di modifica__ allo stato;
- __non fornire metodi di modifica__ allo stato;
- avere tutti gli __attributi privati__ per i tipi potenzialmente mutabili (come `List<T>`);
- avere tutti gli __attributi privati__ per i tipi potenzialmente mutabili (come `List<T>`) e fornire solo il valore tramite i _getter_ e non la referenza;
- avere tutti gli __attributi final__ se non già privati;
- avere tutti gli __attributi final__ se non già privati;
- assicurare l'__accesso esclusivo__ a tutte le parti non mutabili, ovvero non avere reference escaping.
- assicurare l'__accesso esclusivo__ a tutte le parti non mutabili, ovvero non avere reference escaping.
## Code smell
## Code smell
I _code smell_ sono dei segnali che suggeriscono problemi nella progettazione del codice.
I _code smell_ sono dei segnali, della _"puzza"_, che suggeriscono problemi nella progettazione del codice.
Di seguito ne sono elencati alcuni:
Di seguito ne sono elencati alcuni:
- __codice duplicato__: si può fare per arrivare velocemente al verde ma è da togliere con il refactoring.
- __codice duplicato__: si può fare per arrivare velocemente al verde ma è da togliere con il refactoring. Rischia di portarsi dietro degli errori o particolarità legate al applicazione originale di questo codice. È dunque importante cercare di fattorizzare il più possibile.
Le parti di codice in comune possono quindi essere fattorizzate.
- __metodi troppo lunghi__: non è un vincolo "_hard_" dato che dipende dai casi ma solitamente sono poco leggibili e poco riusabili;
- __metodi troppo lunghi__: sono poco leggibili e poco riusabili;
- __troppi livelli di indentazione__: scarsa leggibilità e riusabilità, è bene fattorizzare il codice, invece che avere una serie di if e for _innestati_ che portano a cofunsione e quind possibili errori è meglio creare dei metodi con nomi chiari.
- __troppi livelli di indentazione__: scarsa leggibilità e riusabilità, è bene fattorizzare il codice;
- __troppi attributi__: suggerisce che la classe non rispetta la single responsability, ovvero fa troppe cose;
- __troppi attributi__: suggerisce che la classe non rispetta la single responsability, ovvero fa troppe cose;
- __lunghe sequenze di _if-else_ o _switch___;
- __lunghe sequenze di _if-else_ o _switch___;
- __classe troppo grande__;
- __classe troppo grande__;
- __lista parametri troppo lunga__;
- __lista parametri troppo lunga__: se proprio ne ho bisogno meglio raggrupparli in una struttura e passarli come unico parametro;
- __numeri _magici___: è importante assegnare alle costanti numeriche all'interno del codice un nome per comprendere meglio il loro scopo;
- __numeri _magici___: è importante assegnare alle costanti numeriche all'interno del codice un nome per comprendere meglio il loro scopo, infatti dei semplici numeri possono avere significati diversi in base al loro contesto, ad esempio uno zero puù indicare il suo valore numerico, l'assenza di valori o NULL;
- __commenti che spiegano cosa fa il codice__: indicazione che il codice non è abbastanza chiaro;
- __commenti che spiegano cosa fa il codice__: indica/ammette che il codice non è abbastanza chiaro;
- __nomi oscuri o inconsistenti__;
- __nomi oscuri o inconsistenti__;
- __codice morto__: nel programma non deve essere presente del codice irraggiungibile o commentato. Utilizzando strumenti di versioning è possibile riaccedere a codice precedentemente scritto con facilità.
- __codice morto__: nel programma non deve essere presente del codice irraggiungibile o commentato o non testato. Questo mi appesantisce il progetto o porta possibili rischi. È quindi meglio eliminarlo, se mai dovesse tornarmi utile potrò recuperarlo utilizzando strumenti di versioning, accedendo a commit precedenti alla sua cancellazione.
- __getter e setter__: vedi principio di __tell don't ask__.
- __getter e setter__: Sono utili ma bisogna capire se ne ho davvero necessità o posso fornire piuttosto dei metodi che facciano ciò che poi l'utente farà con il dato ottenuto, vedi principio di __tell don't ask__ nella prossima sezione.