├── README.md ├── SUMMARY.md ├── avvertenze-ed-euristiche.md ├── classi.md ├── commenti.md ├── delimitazioni.md ├── esempi ├── args.md ├── comparisoncompactor.md ├── primegenerator.md └── primeprinter.md ├── formattazione.md ├── funzioni.md ├── gestione-degli-errori.md ├── nomi-significativi.md ├── oggetti-e-strutture.md ├── simple-design.md ├── sistemi.md └── unit-test.md /README.md: -------------------------------------------------------------------------------- 1 | # Clean Code 2 | 3 | Questo book ha lo scopo di riassumere in breve tutte le best practices presenti all'interno del libro "Clean Code" di Robert C. Martin \(Uncle Bob\). Di seguito sono presenti le sezioni per ogni tipo di argomento trattato all'interno del libro. 4 | 5 | ### Tabella dei contenuti 6 | 7 | 1. [Nomi significativi](https://mirkorap16.gitbook.io/clean-code/nomi-significativi) 8 | 2. [Funzioni](https://mirkorap16.gitbook.io/clean-code/funzioni) 9 | 3. [Commenti](https://mirkorap16.gitbook.io/clean-code/commenti) 10 | 4. [Formattazione](https://mirkorap16.gitbook.io/clean-code/formattazione) 11 | 5. [Oggetti e strutture](https://mirkorap16.gitbook.io/clean-code/oggetti-e-strutture) 12 | 6. [Gestione degli errori](https://mirkorap16.gitbook.io/clean-code/gestione-degli-errori) 13 | 7. [Delimitazioni](https://mirkorap16.gitbook.io/clean-code/delimitazioni) 14 | 8. [Unit test](https://mirkorap16.gitbook.io/clean-code/unit-test) 15 | 9. [Classi](https://mirkorap16.gitbook.io/clean-code/classi) 16 | 10. [Sistemi](https://mirkorap16.gitbook.io/clean-code/sistemi) 17 | 11. [Simple Design](https://mirkorap16.gitbook.io/clean-code/simple-design) 18 | 12. [Avvertenze ed euristiche](https://mirkorap16.gitbook.io/clean-code/avvertenze-ed-euristiche) 19 | 20 | ### Esempi 21 | 22 | 1. [PrimeGenerator](https://mirkorap16.gitbook.io/clean-code/esempi/primegenerator) 23 | 2. [PrimePrinter](https://mirkorap16.gitbook.io/clean-code/esempi/primeprinter) 24 | 3. [Args](https://mirkorap16.gitbook.io/clean-code/esempi/args) 25 | 4. [ComparisonCompactor](https://mirkorap16.gitbook.io/clean-code/esempi/comparisoncompactor) 26 | 27 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Clean Code](README.md) 4 | * [Nomi significativi](nomi-significativi.md) 5 | * [Funzioni](funzioni.md) 6 | * [Commenti](commenti.md) 7 | * [Formattazione](formattazione.md) 8 | * [Oggetti e strutture](oggetti-e-strutture.md) 9 | * [Gestione degli errori](gestione-degli-errori.md) 10 | * [Delimitazioni](delimitazioni.md) 11 | * [Unit test](unit-test.md) 12 | * [Classi](classi.md) 13 | * [Sistemi](sistemi.md) 14 | * [Simple Design](simple-design.md) 15 | * [Avvertenze ed euristiche](avvertenze-ed-euristiche.md) 16 | 17 | ## Esempi 18 | 19 | * [PrimeGenerator](esempi/primegenerator.md) 20 | * [PrimePrinter](esempi/primeprinter.md) 21 | * [Args](esempi/args.md) 22 | * [ComparisonCompactor](esempi/comparisoncompactor.md) 23 | 24 | -------------------------------------------------------------------------------- /avvertenze-ed-euristiche.md: -------------------------------------------------------------------------------- 1 | # Avvertenze ed euristiche 2 | 3 | ## Commenti 4 | 5 | ### Commenti inappropriati 6 | 7 | Un commento non dovrebbe contenere informazioni inutili quali: la cronologia delle modifiche, l'autore del file, la data di creazione e di ultima modifica. Tutte queste informazioni, ormai, vengono salvate dai sistemi di controllo versione. 8 | 9 | ### Commenti obsoleti 10 | 11 | I commenti invecchiano rapidamente e ben presto diventano imprecisi e obsoleti. Se trovate un commento obsoleto, è meglio aggiornarlo o eliminarlo. 12 | 13 | ### Commenti ridondanti 14 | 15 | Un commento è ridondante se descrive qualcosa di ovvio e già esplicativo. 16 | 17 | ```text 18 | $i++; // incrementa $i 19 | ``` 20 | 21 | Un altro esempio sono i commenti che riportano la signature e il valore di ritorno del metodo 22 | 23 | ```text 24 | /** 25 | * @param Item $item 26 | * @return void 27 | */ 28 | public function sellItem(Item $item): void { ... } 29 | ``` 30 | 31 | ### Commenti mal scritti 32 | 33 | I commenti devono essere scritti con attenzione. Non divagate. Non raccontate ovvietà. Siate concisi. 34 | 35 | ### Codice commentato 36 | 37 | Il codice commentato se ne sta lì e "marcisce". Nessuno lo toccherà perché tutti penseranno che sia importante. Quando vedete del codice commentato, **cancellatelo!** Non temete, il sistema di controllo versione continuerà a ricordarlo. 38 | 39 | ## Ambiente 40 | 41 | ### Build che richiedono più di un passo 42 | 43 | La build di un progetto dovrebbe essere un'unica operazione semplice. Non si deve essere costretti a lanciare una serie di script per compilare i singoli elementi. 44 | 45 | ### Test che richiedono più di un passo 46 | 47 | Dovreste essere in grado di eseguire tutti gli unit test con un solo comando. La capacità di eseguire tutti i test è così importante che dovrebbe essere rapida, facile e banale. 48 | 49 | ## Funzioni 50 | 51 | ### Troppi argomenti 52 | 53 | Le funzioni dovrebbero avere un numero limitato di argomenti. Meglio nessun argomento; uno, due o tre argomenti vanno bene. Più di tre è troppo! 54 | 55 | ### Argomenti di output 56 | 57 | Gli argomenti passati per riferimento non sono intuitivi. Se la vostra funzione deve modificare lo stato di qualcosa, dovrà trattarsi dello stato dell'oggetto sul quale viene richiamata. 58 | 59 | ### Flag usati come argomenti 60 | 61 | Gli argomenti booleani dichiarano che la funzione fa più di una cosa. Questo perché ci porta ad avere all'interno della funzione una condizione sia per quando il flag è `true` e sia per quando è `false`. Per tale ragione essi vanno evitati. 62 | 63 | ```text 64 | public function render(bool $canFormat = false): string { 65 | if ($canFormat) { 66 | ... 67 | } else { 68 | ... 69 | } 70 | } 71 | ``` 72 | 73 | ### Funzioni "morte" 74 | 75 | I metodi che non vengono mai richiamati dovrebbero essere eliminati. Non temete, il sistema di controllo versione continuerà a ricordarli. 76 | 77 | ## Generali 78 | 79 | ### Più linguaggi in un file di codice sorgente 80 | 81 | Gli ambienti di programmazione di oggi consentono di inserire più linguaggi in un unico file di codice sorgente. Questo potrebbe essere fonte di confusione. Purtroppo, a volte, è necessario utilizzarne più di uno, ma dovremmo cercare di ridurre al minimo i frammenti di altri linguaggi nel nostro file di codice sorgente. 82 | 83 | ### Non viene implementato un comportamento naturale 84 | 85 | Ogni funzione o classe dovrebbe implementare i comportamenti che un altro programmatore possa aspettarsi. Per esempio, considerate una funzione che traduce il nome di un giorno della settimana in valore numerico: 86 | 87 | ```text 88 | $day = Day::stringToDay($dayName); 89 | ``` 90 | 91 | Ci aspetteremmo che: 92 | 93 | * La stringa "Monday" restituisca 1 \(si tratta del primo giorno della settimana\) 94 | * Venissero tradotte anche le abbreviazioni più comuni \(es.: lun, mar, mer...\) 95 | * Non ci siano distinzioni tra maiuscole e minuscole 96 | 97 | ### Comportamento errato alle delimitazioni 98 | 99 | Ogni condizione, ogni caso limite e ogni dettaglio deve essere controllato. Non contate ciecamente sul vostro intuito. Individuate ogni condizione e scrivete un test apposito. 100 | 101 | ### Disattivazione delle "sicure" 102 | 103 | E' pericoloso disattivare i meccanismi di sicurezza. Disattivare determinati warning del compilatore o commentare determinati test, può produrre estenuanti sessioni di debugging. 104 | 105 | ### Duplicazioni 106 | 107 | Il **"Principio DRY"** \(_Don't Repeat Yourself_\) è uno dei principi più importanti della progettazione del software. Esistono diverse forme con cui si può presentare la duplicazione del codice: 108 | 109 | * Quando si hanno dei frammenti di codice identici 110 | * Quando abbiamo dei costrutti _switch-case_ o _if-else_ che compaiono più volte 111 | * Quando abbiamo algoritmi simili, ma non con le stesse righe di codice 112 | 113 | Ogni volta che troviamo questo tipo di duplicazioni, dobbiamo eliminarle. Nel **primo caso** possiamo creare una funzione che elimina i frammenti di codice identici. Nel **secondo caso** possiamo evitare i costrutti _switch-case_ o _if-else_ attraverso il polimorfismo. Nel **terzo caso** possiamo adottare pattern progettuali come il [**Template Method**](https://it.wikipedia.org/wiki/Template_method) ****o [**Strategy**](https://it.wikipedia.org/wiki/Strategy_pattern). 114 | 115 | ### Codice posto al livello di astrazione errato 116 | 117 | E' importante creare astrazioni che separano i concetti generali di alto livello dai concetti dettagliati di basso livello. Quando ci comportiamo in questo modo, dobbiamo fare in modo che **tutti i concetti di basso livello si trovano nelle classi derivate e che tutti i concetti di alto livello si trovano nelle classi astratte.** Per esempio, le costanti, le variabili e le funzioni che appartengono solo all'implementazione non dovrebbero trovarsi nella classe base. Questa regola riguarda anche i file di codice sorgente, i componenti e i moduli. 118 | 119 | ### Classi base che dipendono dalle loro derivate 120 | 121 | I concetti fra le classi base e derivate devono essere separati e resi indipendenti. In generale, le classi base non dovrebbero conoscere nulla delle loro derivate. 122 | 123 | ### Eccesso di informazioni 124 | 125 | Dobbiamo cercare di limitare il numero di informazioni che esponiamo verso l'esterno. Non create classi con tanti metodi pubblici/protetti. Non create classi con tante variabili di istanza. Nascondete le costanti che non devono essere utilizzate all'esterno della classe. In questa maniera manteniamo le nostre classi leggere e riduciamo al minimo l'accoppiamento. 126 | 127 | ### Il "dead code" 128 | 129 | Il **dead code** è quel codice che non viene mai eseguito. Lo trovate nel corpo di un'istruzione `if` che controlla una condizione che non può mai verificarsi. Lo trovate nel corpo di un metodo che non viene mai richiamato. Quando trovate questo tipo di codice... eliminatelo! 130 | 131 | ### Separazione verticale 132 | 133 | Le variabili e le funzioni dovrebbero essere definite nei pressi di dove vengono usate. 134 | 135 | **Le variabili locali** dovrebbero essere dichiarate appena sopra il loro primo uso. 136 | 137 | **Le funzioni private** dovrebbero essere definite appena sotto il loro primo uso. 138 | 139 | ### Incoerenza 140 | 141 | Se fate qualcosa in un determinato modo, fate anche tutte le cose simili nello stesso modo. Se in una determinata funzione impiegate una variabile dal nome `response` per contenere una `App\Http\Response`, allora usate lo stesso nome in tutte le altre funzioni che usano oggetti di tipo `App\Http\Response` 142 | 143 | ### Congestione 144 | 145 | Variabili inutilizzate, funzioni mai richiamate, commenti non informativi e così via, sono tutte cose che congestionano il codice e perciò devono essere eliminate. 146 | 147 | ### Accoppiamento artificioso 148 | 149 | Le cose che non dipendono le une dalle altre non dovrebbero essere accoppiate artificiosamente. Una variabile, una costante o una funzione inserita in una posizione inappropriata, costringe le altre classi a dipendere da una classe che non ha niente a che vedere con loro. 150 | 151 | ### Una questione di invidia \("feature envy"\) 152 | 153 | I metodi di una classe dovrebbero interessarsi alle variabili della classe a cui appartengono e non alle variabili di altre classi. Ciò è considerato un _code smells_ perché porta il metodo della classe a dover conoscere la struttura e i dati dell'oggetto che sta manipolando. **Agli oggetti dovremmo chiedere di fare qualcosa e non chiedergli i loro dati interni.** Almeno che non abbiamo a che fare con delle **strutture** \(vedi capitolo "[Oggetti e strutture](https://mirkorap16.gitbook.io/clean-code/oggetti-e-strutture)"\). Chiaramente questa regola non è assoluta e talvolta è necessario esporre i dati di una classe per poterli recuperare. 154 | 155 | ### Argomenti a "selettore" 156 | 157 | Gli argomenti a "selettore" sono quel tipo di argomenti che vengono usati per selezionare il comportamento che deve avere la funzione. Questo genere di argomenti, che solitamente sono di tipo `boolean`, vanno evitati, perché costringe a ricordarsi che cosa significa il valore per quel dato argomento. 158 | 159 | ```text 160 | public function getErrors(bool $clear): array { 161 | $messages = $this->getMessages(Message::TYPE_ERROR); 162 | 163 | if ($clear) { 164 | $this->clearByType(Message::TYPE_ERROR); 165 | } 166 | 167 | return $messages; 168 | } 169 | ``` 170 | 171 | Questa funzione utilizza il selettore `$clear` per svuotare i messaggi presenti nella sessione, solo nel caso in cui viene passato il valore `true`. Questo, però, porta a doversi ricordare che esiste questa funzione che, in base al valore passato, consente di pulire o meno la lista dei messaggi. Se proprio non possiamo fare a meno di avere le due funzioni separate, una per recuperare gli errori e l'altra per eliminarli, allora possiamo creare una funzione di questo tipo: 172 | 173 | ```text 174 | public function getErrorsThenClear(): array { 175 | $messages = $this->getErrors(); 176 | $this->clearByType(Message::TYPE_ERROR); 177 | return $messages; 178 | } 179 | ``` 180 | 181 | ### Offuscamento dello scopo 182 | 183 | Il codice deve essere il più espressivo possibile. Le espressioni al volo, la notazione ungherese e i numeri magici offuscano lo scopo del nostro codice. 184 | 185 | ```text 186 | public function m_otCalc(): int { 187 | return $iThsWkd * $iThsRte + (int)round(0.5 * $iThsRte * max(0, $iThsWkd - 400)); 188 | } 189 | ``` 190 | 191 | ### Responsabilità mal collocate 192 | 193 | Una delle responsabilità più importanti di uno sviluppatore è la scelta di dove collocare il codice. Il codice dovrebbe essere collocato là dove chi legge si aspetta di trovarlo. Se dobbiamo creare la funzione per calcolare la paga settimanale di un lavoratore, la collochiamo all'interno della classe `Employee` o nella classe `EmployeePayCalculator`? 194 | 195 | ### Modificatore static inappropriato 196 | 197 | `Math::max($a, $b)` è un buon metodo statico perché non opera su un'unica istanza. Sarebbe assurdo dover dire `new Math()->max($a, $b)` o `$a->max($b)`. Questo perché tutti i dati usati da `Math::max()` provengono dai suoi due argomenti e non da un oggetto "proprietario". In poche parole in nessun caso vogliamo che `Math::max()` sia polimorfica. Tuttavia a volte scriviamo funzioni statiche che non dovrebbero essere statiche: 198 | 199 | ```text 200 | HourlyPayCalculator::calculate($employee, $overtimeRate); 201 | ``` 202 | 203 | Per la funzione `calculate` è ragionevole che possiamo volere che sia polimorfica. Infatti potremmo voler implementare più algoritmi per il calcolo della paga oraria. Pertanto, in questo caso la funzione non dovrebbe essere statica. In generale dovreste preferire i metodi non statici a quelli statici. 204 | 205 | ### Usate variabili descrittive 206 | 207 | Uno dei modi più potenti per rendere leggibile il codice consiste nel suddividere i calcoli in valori intermedi contenuti in variabili con nomi significativi. 208 | 209 | ```text 210 | $data = explode('-', $nameFormatted); 211 | $array[$data[1]] = $data[0]; 212 | ``` 213 | 214 | Cosa vuol dire il codice sopra riportato? Che cos'è `$data[0]` e `$data[1]`? Riscriviamo lo stesso codice in questa maniera: 215 | 216 | ```text 217 | [$firstName, $lastName] = explode('-', $nameFormatted); 218 | $array[$lastName] = $firstName; 219 | ``` 220 | 221 | Ora è molto più chiaro! Sappiamo che `$nameFormatted` è una stringa del tipo "Mario-Rossi" e quindi stiamo costruendo un array in cui la chiave è il cognome e il valore è il nome. 222 | 223 | ### Il nome di una funzione deve essere comunicativo 224 | 225 | Osservate il seguente codice: 226 | 227 | ```text 228 | $newDate = $date->add(5); 229 | ``` 230 | 231 | Che cosa vi aspettate che faccia la funzione `add`? Vi aspettate che aggiunga cinque giorni alla data? O magari settimane o ore? La istanza di `$date` viene modificata o la funzione restituisce un nuovo oggetto `Date` senza modificare il vecchio? E' impossibile capire dalla chiamata che cosa fa la funzione. Se la funzione aggiungesse cinque giorni alla data e la modificasse, allora si dovrebbe chiamare `addDaysTo` o `increaseByDays`. Se invece la funzione restituisce un nuovo oggetto `Date` posto cinque giorni dopo, senza cambiare l'istanza originale, allora la funzione si dovrebbe chiamare `daysLater`. Se siete costretti a dover guardare l'implementazione per capire che cosa fa una funzione, allora vuol dire che dovreste scegliere un nome migliore. 232 | 233 | ### Comprendere l'algoritmo 234 | 235 | Prima di pensare di aver terminato di lavorare su una funzione, assicuratevi di aver capito come essa funziona. Non basta sapere che la funzione "fa il suo lavoro", bisogna anche comprenderne il funzionamento. Se non si riesce a comprenderne il funzionamento, allora vuol dire che bisogna rendere il codice più pulito e chiaro. 236 | 237 | ### Rendere fisiche le dipendenze logiche 238 | 239 | Se un modulo dipende da un altro, tale dipendenza dovrebbe essere fisica, non solo logica. Per esempio, immaginate di voler creare una funzione che stampa un report delle ore di lavoro dei dipendenti. Abbiamo una classe `HourlyReporter` che raccoglie tutti i dati e poi li passa a `HourlyReportFormatter` che si occupa invece della stampa: 240 | 241 | ```text 242 | class HourlyReporter { 243 | private const PAGE_SIZE = 55; 244 | private $formatter; 245 | private $page; 246 | 247 | public function __construct(HourlyReportFormatter $formatter) { 248 | $this->formatter = $formatter; 249 | $this->page = new Collection(); 250 | } 251 | 252 | public function generateReport(array $employees): void { 253 | foreach ($employees as $employee) { 254 | $this->addLineItemToPage($employee); 255 | 256 | if ($this->page->size() === self::PAGE_SIZE) { 257 | $this->printAndClearItemList(); 258 | } 259 | } 260 | 261 | if ($this->page->size() > 0) { 262 | $this->printAndClearItemList(); 263 | } 264 | } 265 | 266 | private function printAndClearItemList(): void { 267 | $this->formatter->format($this->page); 268 | $this->page->clear(); 269 | } 270 | 271 | private function addLineItemToPage(Employee $employee): void { 272 | $item = new LineItem(); 273 | $item->setName($employee->getName()); 274 | $item->setHours($employee->getTenthsWorked() / 10); 275 | $item->setTenths($employee->getTenthsWorked() % 10); 276 | $this->page->add($item); 277 | } 278 | } 279 | ``` 280 | 281 | La costante `PAGE_SIZE` è una dipendenza logica che non è stata resa fisica. Perché `HourlyReporter` dovrebbe conoscere le dimensioni della pagina? Ciò dovrebbe rientrare nelle responsabilità di `HourlyReportFormatter`. Possiamo rimediare rendendo fisica questa dipendenza creando in `HourlyReportFormatter` un nuovo metodo chiamato `getMaxPageSize`. `HourlyReporter` richiamerà questo metodo invece di utilizzare la costante `PAGE_SIZE` 282 | 283 | ### Meglio il polimorfismo di un if/else o di uno switch/case 284 | 285 | Molti usano le istruzioni if/else o switch/case perché è la classica soluzione a forza bruta e non perché esse siano la soluzione più adatta alla situazione. Pertanto, ogni volta che vediamo un'istruzione di questo tipo, ricordiamoci di considerare il polimorfismo come una possibile alternativa. Inoltre, cerchiamo di rispettare la regola del **One Switch:** 286 | 287 | {% hint style="info" %} 288 | Non ci deve mai essere più di un'istruzione switch per un determinato tipo di selezione. I case in tale istruzione switch devono creare oggetti polimorfici, in modo tale da evitare di ripetere lo stesso switch/case in altri punti del sistema. 289 | {% endhint %} 290 | 291 | ### Seguite convenzioni standard 292 | 293 | Ogni team dovrebbe seguire uno standard di programmazione basato su norme convenzionali del settore. Ogni membro del team deve accettare e seguire lo stesso standard deciso da tutto il team. 294 | 295 | ### Al posto dei "numeri magici", usate costanti "parlanti" 296 | 297 | Non è una buona idea avere dei numeri grezzi all'interno del codice. Ciò non solo rende difficile riconoscerli, ma rende difficile anche trovare tutte le loro occorrenze. Pertanto, al loro posto, dovreste utilizzare delle costanti dal nome significativo. 298 | 299 | ```text 300 | const MAX_SCHEDULE_PER_DAY = 10; 301 | const SECONDS_PER_DAY = 86400; 302 | const LINES_PER_PAGE = 50; 303 | const PROCESSING_ORDER_STATUS = 'processing'; 304 | const DEFAULT_HOST = 'localhost'; 305 | ``` 306 | 307 | ### Siate precisi 308 | 309 | Quando prendete una decisione nel vostro codice, cercate di essere precisi. Non siate pigri nella precisione delle vostre decisioni. Se richiamate una funzione che potrebbe restituire `null`, occupatevi di quel valore. Se richiamate una funzione che potrebbe lanciare un'eccezione, implementate un meccanismo per gestire le eccezioni. Questo tipo di imprecisioni dovrebbero essere eliminate. 310 | 311 | ### Il principio "structure over convention" 312 | 313 | Esplicitate le decisioni progettuali con la struttura piuttosto che con una semplice convenzione. Le convenzioni di denominazione vanno bene, ma sono comunque inferiori rispetto ad una struttura progettuale che imponga la regola. 314 | 315 | ### Incapsulate i costrutti condizionali 316 | 317 | La logica booleana a volte è difficile da comprendere. Pertanto create delle funzioni che spieghino lo scopo del costrutto condizionale. 318 | 319 | ```text 320 | if ($this->shouldBeDeleted($timer)) { ... } 321 | 322 | è meglio rispetto a: 323 | 324 | if ($timer->hasExpired() && !$timer->isRecurrent()) { ... } 325 | ``` 326 | 327 | ### Evitate i negativi nei costrutti condizionali 328 | 329 | I costrutti condizionali negativi sono più difficili da comprendere rispetto a quelli positivi. 330 | 331 | ```text 332 | if ($this->shouldBeDeleted($timer)) { ... } 333 | 334 | è più semplice da capire rispetto a: 335 | 336 | if (!$this->shouldNotBeDeleted($timer)) { ... } 337 | ``` 338 | 339 | ### Le funzioni dovrebbero fare una cosa sola 340 | 341 | Le funzioni che fanno più di una cosa dovrebbero essere convertite in funzioni più piccole, ognuna delle quali fa una cosa soltanto. 342 | 343 | ```text 344 | public function pay(): void { 345 | foreach ($this->employees as $e) { 346 | if ($e->isPayDay()) { 347 | $pay = $e->calculatePay(); 348 | $e->deliverPay($pay); 349 | } 350 | } 351 | } 352 | ``` 353 | 354 | Questa funzione fa tre cose: 355 | 356 | * Cicla tutti i dipendenti 357 | * Controlla se il dipendente deve essere pagato 358 | * Paga il dipendente 359 | 360 | Possiamo dividere questa funzione nel seguente modo: 361 | 362 | ```text 363 | public function pay(): void { 364 | foreach ($this->employees as $e) { 365 | $this->payIfNecessary($e); 366 | } 367 | } 368 | 369 | private function payIfNecessary(Employee $e): void { 370 | if ($e->isPayDay()) { 371 | $this->calculateAndDeliverPay($e); 372 | } 373 | } 374 | 375 | private function calculateAndDeliverPay(Employee $e): void { 376 | $pay = $e->calculatePay(); 377 | $e->deliverPay($pay); 378 | } 379 | ``` 380 | 381 | ### Accoppiamenti temporali nascosti 382 | 383 | Gli accoppiamenti temporali possono essere necessari, ma non nascondeteli. 384 | 385 | ```text 386 | class MoogDiver { 387 | private $gradient; 388 | private $splines = []; 389 | 390 | public function dive(string $reason): void { 391 | $this->saturateGradient(); 392 | $this->reticulateSplines(); 393 | $this->diveForMoog($reason); 394 | } 395 | 396 | private function saturateGradient(): void { ... } 397 | 398 | private function reticulateSplines(): void { ... } 399 | 400 | private function diveForMoog(string $reason): void { ... } 401 | } 402 | ``` 403 | 404 | L'ordine delle tre funzioni è importante. Dobbiamo prima saturare il gradiente, dopo dobbiamo produrre il reticolo delle spline e infine passare tutto alla funzione `diveForMoog`. Questo accoppiamento temporale non si capisce bene. Potremmo riscrivere le funzioni nel seguente modo: 405 | 406 | ```text 407 | class MoogDiver { 408 | private $gradient; 409 | private $splines = []; 410 | 411 | public function dive(string $reason): void { 412 | $this->saturateGradientAndReticulateSplines(); 413 | $this->diveForMoog($reason); 414 | } 415 | 416 | private function saturateGradientAndReticulateSplines(): void { 417 | // codice per saturare il gradiente 418 | $this->reticulateSplines(); 419 | } 420 | 421 | private function reticulateSplines(): void { ... } 422 | 423 | private function diveForMoog(string $reason): void { ... } 424 | } 425 | ``` 426 | 427 | ### Non siate arbitrari 428 | 429 | Abbiate un motivo per scegliere la struttura del vostro codice e assicuratevi che tale motivo sia comunicato adeguatamente dalla struttura del codice. Se la sua struttura appare arbitraria, gli altri si sentiranno in diritto di cambiarla e di migliorarla. 430 | 431 | ### Incapsulate le condizioni di delimitazione 432 | 433 | Le condizioni di delimitazione sono difficili da comprendere. Incapsulatele in un solo luogo e **non** sparpagliatele in tutto il codice. 434 | 435 | ```text 436 | if ($level + 1 < count($tags)) { 437 | $parts = new Parse($body, $tags, $level + 1); 438 | $body = null; 439 | } 440 | ``` 441 | 442 | Dall'esempio sopra riportato compare per ben due volte `$level + 1`. Questa condizione di delimitazione dovrebbe essere incapsulata nel seguente modo: 443 | 444 | ```text 445 | $nextLevel = $level + 1; 446 | if ($nextLevel < count($tags)) { 447 | $parts = new Parse($body, $tags, $nextLevel); 448 | $body = null; 449 | } 450 | ``` 451 | 452 | ### Le funzioni devono discendere un solo livello di astrazione 453 | 454 | Le istruzioni presenti all'interno di una funzione dovrebbero tutte trovarsi allo stesso livello di astrazione, il quale dovrebbe essere esattamente un livello sotto l'operazione descritta dal nome della funzione. 455 | 456 | ```text 457 | public function render(): string { 458 | $html = new StringBuffer('size > 0) { 460 | $html->append(' size="')->append($size + 1)->append('"'); 461 | } 462 | $html->append('>'); 463 | return $html->toString(); 464 | } 465 | ``` 466 | 467 | Questa funzione presenta due livelli di astrazione. Il primo è il concetto che una linea orizzontale abbia uno spessore \($size\). Il secondo è la sintassi del tag HR. Rifattorizziamo la funzione in modo tale che abbia un solo livello di astrazione: 468 | 469 | ```text 470 | public function render(): string { 471 | $hr = new HtmlTag('hr'); 472 | if ($this->size > 0) { 473 | $hr->addAttribute('size', $this->calculateHeight()); 474 | } 475 | return $hr->html(); 476 | } 477 | 478 | private function calculateHeight(): string { 479 | $height = $this->size + 1; 480 | return sprintf('%d', $height); 481 | } 482 | ``` 483 | 484 | ### Mantenete ai livelli più elevati i dati configurabili 485 | 486 | Se avete una costante, come un default o un valore di configurazione, la quale è nota e prevista a un livello di astrazione superiore, non seppellitela in una classe che si trova ad un basso livello di astrazione. Spostatela in alto, così che essa possa essere "diffusa" ai livelli sottostanti dell'applicazione. 487 | 488 | ### Evitate la navigazione transitiva 489 | 490 | Evitate di far conoscere ai vostri moduli l'intera mappa di navigazione del sistema. Se il modulo A utilizza B e il modulo B utilizza C, non vogliamo che il modulo A conosca e utilizza C. Non vogliamo avere catene di chiamate come la seguente: `$a->getB()->getC()->doSomething();`. Questa è chiamata [_Legge di Demetra_](https://mirkorap16.gitbook.io/clean-code/oggetti-e-strutture#la-legge-di-demetra). Piuttosto vogliamo che i nostri moduli ci offrano direttamente ciò di cui abbiamo bisogno. Dovremmo semplicemente poter dire: `$a->doSomething();` 491 | 492 | ## Nomi 493 | 494 | ### Scegliete nomi descrittivi 495 | 496 | Nel software, i nomi contribuiscono per il 90% alla leggibilità del codice. Pertanto sceglieteli sempre con grande cura. Attraverso l'utilizzo di buoni nomi, coloro che leggeranno il codice capiranno subito qual è il contenuto di una variabile, che cosa fa la funzione o ancora qual è lo scopo della classe. 497 | 498 | ### Scegliete nomi collocati al corretto livello di astrazione 499 | 500 | Scegliete dei nomi che riflettano il livello di astrazione della classe o della funzione nella quale state lavorando. Considerate l'interfaccia seguente: 501 | 502 | ```text 503 | interface Modem { 504 | public function dial(string $phoneNumber): bool; 505 | public function disconnect(): bool; 506 | public function send(string $c): bool; 507 | public function recv(): string; 508 | public function getConnectedPhoneNumber(): string; 509 | } 510 | ``` 511 | 512 | L'interfaccia sopra presenta dei nomi collocati ad un livello di astrazione errato, perché da per scontato che i modem si connetteranno componendo il numero, ma potrebbero esserci modem che invece si connettono tramite cavo. Pertanto rinominiamo le funzioni nel seguente modo: 513 | 514 | ```text 515 | interface Modem { 516 | public function connect(string $connectionLocator): bool; 517 | public function disconnect(): bool; 518 | public function send(string $c): bool; 519 | public function recv(): string; 520 | public function getConnectedLocator(): string; 521 | } 522 | ``` 523 | 524 | ### Usate la nomenclatura standard 525 | 526 | I nomi sono più facili da comprendere se si basano su una convenzione. Per esempio, se utilizzate il pattern DECORATOR, potete aggiungere la parola `Decorator` come suffisso nel nome delle classi di "decorazione". Spesso i team sviluppano un proprio standard di denominazione per un determinato progetto. Questo faciliterà nella comprensione coloro che leggeranno il codice. 527 | 528 | ### Nomi non ambigui 529 | 530 | Scegliete dei nomi che non introducano ambiguità nel comportamento di una funzione o di una variabile. Piuttosto utilizzate dei nomi un po' più lunghi se questo vi aiuta ad enfatizzare il comportamento della funzione. 531 | 532 | ### Usate nomi lunghi per grandi campi di visibilità \(scope\) 533 | 534 | La lunghezza di un nome dovrebbe essere correlata all'estensione del livello di visibilità \(scope\). Potete usare nomi corti per le variabili con scope molto limitato, ma se la variabile risulta ampiamente visibile, dovreste usare nomi lunghi. 535 | 536 | Nell'esempio seguente ho utilizzato un nome molto corto per indicare un dipendente `$e`, questo perché la variabile ha uno scope molto limitato. 537 | 538 | ```text 539 | public function pay() { 540 | foreach ($this->employees as $e) { 541 | $this->payIfNecessary($e); 542 | } 543 | } 544 | ``` 545 | 546 | ### Evitate le codifiche 547 | 548 | I nomi non dovrebbero contenere una codifica del tipo: `$_`, `$o_user`, `f_` ecc. Proteggete i vostri nomi dalla notazione ungherese. 549 | 550 | ### I nomi dovrebbero descrivere anche gli effetti collaterali 551 | 552 | I nomi dovrebbero descrivere tutto quello che una variabile, una funzione o una classe è o fa. Non usate un semplice verbo per descrivere una funzione che svolge più di un'azione. 553 | 554 | ```text 555 | public function getCollection(): Collection { 556 | if ($this->collection === null) { 557 | $this->collection = new Collection(); 558 | } 559 | return $this->collection; 560 | } 561 | ``` 562 | 563 | La funzione sopra riportata fa più di un'azione: crea una collection, se non è stata ancora creata, e la restituisce. Pertanto dovrebbe essere rinominata in `createCollectionIfNotExists` 564 | 565 | ## Test 566 | 567 | ### Test insufficienti 568 | 569 | I test, soprattutto quelli unitari, dovrebbero coprire tutta la codebase! I test sono insufficienti ogni volta che ignorano anche solo un'unica condizione. 570 | 571 | ### Usate uno strumento di copertura 572 | 573 | I report degli strumenti di copertura aiutano a scovare le parti di codice che non sono state sottoposte a test. Usateli per avere una copertura totale del vostro codice. 574 | 575 | ### Non saltate i test che considerate banali 576 | 577 | Sono facili da scrivere e il loro valore documentario è maggiore dell'impegno necessario per realizzarli. 578 | 579 | ### Verificate le condizioni di delimitazione 580 | 581 | Sottoponete a test anche le condizioni di delimitazione. Spesso è lì che si nascondono dei comportamenti indesiderati. 582 | 583 | ### Applicate test esaustivi in prossimità dei bug 584 | 585 | Quando trovate un bug in una funzione, è saggio svolgere un test esaustivo su tale funzione. 586 | 587 | ### I test aiutano a diagnosticare i problemi 588 | 589 | Talvolta potete diagnosticare un problema semplicemente osservando i casi di test che falliscono. Questo è un motivo in più per far sì che i casi di test siano il più possibile completi. 590 | 591 | ### I test dovrebbero essere veloci 592 | 593 | Un test lento è un test che non verrà eseguito volentieri. Pertanto fate tutto il possibile per curare la velocità dei vostri test. 594 | 595 | -------------------------------------------------------------------------------- /classi.md: -------------------------------------------------------------------------------- 1 | # Classi 2 | 3 | L'attenzione che dedichiamo nella pulizia delle nostre funzioni e dei nostri blocchi di codice deve essere altrettanto dedicata anche nell'organizzazione delle nostre classi. 4 | 5 | ### Organizzazione delle classi 6 | 7 | Le classi dovrebbero seguire la struttura seguente: 8 | 9 | ```text 10 | class NomeClasse { 11 | // Elenco costanti pubbliche 12 | public const MY_PUBLIC_CONST = 'value'; 13 | 14 | // Elenco costanti protette 15 | protected const MY_PROTECTED_CONST = 'value'; 16 | 17 | // Elenco costanti private 18 | private const MY_PRIVATE_CONST = 'value'; 19 | 20 | // Elenco proprietà pubbliche 21 | public $myPublicProperty; 22 | 23 | // Elenco proprietà protette 24 | protected $myProtectedProperty; 25 | 26 | // Elenco proprietà private 27 | private $myPrivateProperty; 28 | 29 | // Costruttore 30 | public function __construct() { 31 | // ... 32 | } 33 | 34 | // Elenco dei metodi seguendo la "regola dei passi" 35 | } 36 | ``` 37 | 38 | ### Le classi dovrebbero essere piccole! 39 | 40 | Cosa si intende per classe "piccola"? Con le funzioni abbiamo dato una definizione semplicemente contando le righe di codice, con le classi invece dobbiamo usare un'altra metrica, ovvero la **responsabilità**. Una classe che presenta 70 metodi è senza dubbio una classe eccessivamente grande, ma la classe seguente secondo voi è piccola o grande? 41 | 42 | ```text 43 | class SuperDashboard { 44 | public function getLastFocusedComponent(): Component { 45 | // ... 46 | } 47 | 48 | public function setLastFocusedComponent(Component $component): void { 49 | // ... 50 | } 51 | 52 | public function getMajorVersionNumber(): int { 53 | // ... 54 | } 55 | 56 | public function getMinorVersionNumber(): int { 57 | // ... 58 | } 59 | 60 | public function getBuildNumber(): int { 61 | // ... 62 | } 63 | } 64 | ``` 65 | 66 | Nonostante abbia solo 5 metodi questa classe è troppo grande, perché ha troppe responsabilità. Il nome di una classe dovrebbe già descrivere le sue responsabilità. Se non riusciamo a dare un nome conciso per una classe, significa che essa è troppo grande. Per esempio i nomi che includono parole come `Processor` o `Manager` spesso nascondono un eccesso di responsabilità. Dovremmo essere in grado di scrivere una breve descrizione della classe usando al massimo 25 parole, senza mai usare "se", "e", "o" e "ma". Nel caso della classe `SuperDashboard` diremmo: 67 | 68 | > Le classe SuperDashboard fornisce l'accesso al componente che per ultimo aveva il focus **e** ci consente anche di risalire al numero di versione e di build. 69 | 70 | La prima "e" già ci fa capire che questa classe ha troppe responsabilità. 71 | 72 | ### Il principio SRP \(Single Responsibility Principle\) 73 | 74 | Tale principio stabilisce che una classe dovrebbe avere un solo motivo per dover cambiare. La classe `SuperDashboard` ha due motivi per dover cambiare: 75 | 76 | 1. Quando aggiorniamo la versione del software 77 | 2. Quando dobbiamo gestire diversamente il componente focussato 78 | 79 | Possiamo quindi estrarre da `SuperDashboard` un'altra classe che si occupa di gestire la sola versione del software. 80 | 81 | ```text 82 | class Version { 83 | public function getMajorVersionNumber(): int { 84 | // ... 85 | } 86 | 87 | public function getMinorVersionNumber(): int { 88 | // ... 89 | } 90 | 91 | public function getBuildNumber(): int { 92 | // ... 93 | } 94 | } 95 | ``` 96 | 97 | Il principio SRP è tra i più semplici dei principi [S.O.L.I.D](https://it.wikipedia.org/wiki/SOLID), ma anche il più violato. Questo perché ci concentriamo più sul "far funzionare" il software rispetto a "rendere pulito" il software. Entrambi sono degli aspetti importanti in egual misura. Un altro motivo che ci spinge a non seguire questo principio è perché temiamo che la presenza di tante piccole classi mono-scopo complichi la comprensione del software. Tuttavia usare un sistema con tante piccole classi non ha più meccanismi rispetto ad un sistema con poche grandi classi. Al contrario, un sistema con poche grandi classi ci costringerà a vedere parti di codice che fondamentalmente nemmeno ci interessano in quel momento. 98 | 99 | ### Coesione 100 | 101 | Le classi dovrebbero avere un piccolo numero di variabili di istanza. Ognuno dei metodi di una classe dovrebbe manipolare una o più di queste variabili. In generale più variabili vengono manipolate da un metodo e maggiore è la **coesione** fra tale metodo e la sua classe. Dobbiamo puntare ad avere una elevata coesione tra la classe e i suoi metodi. La strategia che punta ad avere funzioni piccole con pochi parametri, porta a una proliferazione di variabili di istanza che vengono utilizzate solo da un sottoinsieme di metodi. Questo va a discapito della coesione. Quando si verifica ciò, significa quasi sempre che è necessario dividere la classe in due o più classi. 102 | 103 | ### Curando la coesione si generano tante piccole classi 104 | 105 | Il fatto di suddividere le funzioni più grandi in funzioni più piccole genera una proliferazione di classi. Immaginate di avere una funzione grande con dichiarate all'interno tante variabili locali, questa funzione va indubbiamente spezzata in funzioni più piccole le quali necessitano di condividere queste variabili dichiarate localmente. Cosa facciamo? Passiamo queste variabili come argomento? Assolutamente no! Possiamo allora dichiariararle come variabili di istanza. Ben presto questo porterà ad un accumulo di variabili di istanza utilizzate solo da un sottoinsieme di metodi. E' arrivato quindi il momento di suddividere la classe in due o più classi, in modo tale da aumentare la coesione tra i metodi, la classe e le sue variabili di istanza. Vedete quindi come dalla rifattorizzazione di un metodo siamo passati alla rifattorizzazione dell'intera classe. 106 | 107 | ### Organizzare gli interventi di modifica 108 | 109 | I software sono soggetti a continui cambiamenti e il rischio che un cambiamento possa introdurre dei bug è sempre alto. Immaginate di avere una classe `Sql` che si occupi di generare stringhe SQL. 110 | 111 | ```text 112 | class Sql { 113 | public function __construct(string $table, array $columns) { ... } 114 | public function create(): string { ... } 115 | public function insert(array $fields): string { ... } 116 | public function select(): string { ... } 117 | public function findByKey(string $key, string $value): string { ... } 118 | } 119 | ``` 120 | 121 | Immaginate che in futuro volessimo aggiungere anche il metodo per gestire l'istruzione `update`. Dovremmo "aprire" la classe e modificarla. Questo però va contro un altro principio [S.O.L.I.D](https://it.wikipedia.org/wiki/SOLID), ovvero l'Open-Closed Principle \(OCP\). Il quale ci dice che le classi dovrebbero essere aperte alle estensioni, ma chiuse alle modifiche. Per aderire a questo principio dobbiamo rifattorizzare la classe in questa maniera: 122 | 123 | ```text 124 | abstract class Sql { 125 | public function __construct(string $table, array $columns) { ... } 126 | abstract public function generate(): string; 127 | } 128 | 129 | class CreateSql extends Sql { 130 | public function generate(): string { ... } 131 | } 132 | 133 | class InsertSql extends Sql { 134 | public function __construct(string $table, array $columns, array $fields) { ... } 135 | public function generate(): string { ... } 136 | } 137 | 138 | class SelectSql extends Sql { 139 | public function generate(): string { ... } 140 | } 141 | 142 | class FindByKeySql extends Sql { 143 | public function __construct(string $table, array $columns, string $key, string $value) { ... } 144 | public function generate(): string { ... } 145 | } 146 | ``` 147 | 148 | In questa maniera il codice non solo diventa molto più semplice da comprendere e da testare, ma aderisce perfettamente al principio OCP. Se domani avessimo la necessità di gestire l'istruzione `update` basterà creare la classe `UpdateSql`. 149 | 150 | Aggiungere delle nuove funzionalità in un codice così strutturato diventa più semplice e ci toglie la paura di introdurre bug nelle altre parti del sistema. 151 | 152 | ### Isolamento delle modifiche 153 | 154 | In OOP sappiamo che le classi concrete contengono i dettagli implementativi, mentre le classi astratte rappresentano solo i concetti. Una classe client dovrebbe dipendere dalle astrazioni e **non** dai dettagli concreti. Questo perché se tali dettagli dovessero cambiare, la classe client subirebbe dei "rischi" portati da questi cambiamenti. Dobbiamo cercare di isolare e disaccoppiare il nostro codice, in questo modo i cambiamenti apportati all'interno di una classe non influiscono sul comportamento delle altre classi. Le nostre classi obbediranno così ad un altro principio [S.O.L.I.D](https://it.wikipedia.org/wiki/SOLID), ovvero il Dependency Inversion Principle \(DIP\). Il quale ci dice che le classi dovrebbero dipendere da astrazioni e non da dettagli concreti. 155 | 156 | ```text 157 | interface StockExchange { 158 | public function getCurrentPrice(string $symbol): Money; 159 | } 160 | 161 | class MilanStockExchange implements StockExchange { 162 | public function getCurrentPrice(string $symbol): Money { ... } 163 | } 164 | 165 | class Portfolio { 166 | private $exchange; 167 | 168 | public function __construct(StockExchange $exchange) { 169 | $this->exchange = $exchange; 170 | } 171 | 172 | // ... 173 | } 174 | ``` 175 | 176 | -------------------------------------------------------------------------------- /commenti.md: -------------------------------------------------------------------------------- 1 | # Commenti 2 | 3 | I commenti sono uno strumento che, se utilizzato con parsimonia, ci permette di rendere più chiare determinate parti del codice, ma, se utilizzato con eccesso, può sortire l'effetto contrario ed indurre a scrivere codice confusionario e fuorviante. Il motivo principale del perché ciò accade è che il codice, essendo in continua evoluzione, rende difficile tenere dei commenti costantemente aggiornati. 4 | 5 | ### I commenti non bastano a migliorare il codice 6 | 7 | Non scrivete commenti solo per migliorare il codice presente. E' molto meglio avere un codice pulito, chiaro e con pochi commenti, piuttosto che un codice intricato e super commentato. 8 | 9 | ### Spiegatevi nel codice 10 | 11 | E' molto meglio scrivere una funzione in più che spiega chiaramente il nostro intento, piuttosto che utilizzare un commento. 12 | 13 | Prima del refactoring: 14 | 15 | ```text 16 | // Restituisce true se lo studente ha la possibilità di accedere alla borsa di studio 17 | if ( 18 | $student->getAge() > 15 && 19 | $student->getAverageGrade() > 8.0 && 20 | $student->getSchool()->getName() === 'I.T.I.S Archimede' 21 | ) { 22 | // ... 23 | } 24 | ``` 25 | 26 | Dopo il refactoring: 27 | 28 | ```text 29 | if ($student->canReceiveScholarship()) { 30 | // ... 31 | } 32 | ``` 33 | 34 | ### Buoni commenti 35 | 36 | Ci sono alcuni casi in cui un commento può essere realmente utile. Ecco quali: 37 | 38 | ### Commenti legali 39 | 40 | I commenti contenenti le note legali talvolta sono necessari in determinate realtà aziendali. Commenti di questo tipo non devono però contenere pagine e pagine di note legali, ma semplicemente il tipo di licenza utilizzata e un collegamento esterno sulle condizioni d'uso. 41 | 42 | ### Descrizione dell'intento 43 | 44 | Talvolta è utile inserire un commento che spieghi il motivo di una determinata decisione. 45 | 46 | ```text 47 | public function sendBulkEmails(array $recipients): void { 48 | foreach ($recipients as $recipient) { 49 | $this->sendEmail($recipient); 50 | // Delay per evitare il sovraccarico del provider 51 | sleep(self::DELAY_BETWEEN_EMAILS); 52 | } 53 | } 54 | 55 | public function sendEmail(User $recipient): void { 56 | $this->mailer->setSubject($this->emailTemplate->getSubject()) 57 | ->setFrom($this->emailTemplate->getFrom()) 58 | ->setTo($recipient->getEmail()) 59 | ->setBody($this->emailTemplate->toHtml()) 60 | ->send(); 61 | } 62 | ``` 63 | 64 | ### Commenti di chiarifica 65 | 66 | Commenti di questo tipo talvolta sono necessari per chiarire strani comportamenti. In generale è meglio trovare un altro modo per chiarire un determinato comportamento, ma se questo non è possibile farlo \(es. codice proveniente da una libreria esterna\), può essere utile lasciare commenti di questo tipo. 67 | 68 | ```text 69 | // $status === self::PAGE_SUCCESS 70 | if (assertTrue($status, self::PAGE_SUCCESS) === 0) { 71 | // ... 72 | } 73 | ``` 74 | 75 | ### Commenti di avvertenza 76 | 77 | A volte è utile lasciare un commento per avvertire gli altri programmatori su determinate conseguenze. 78 | 79 | ```text 80 | /** 81 | * @deprecated Don't use this class to stock products if you use 2.3.0+ version 82 | * @see MultiStoreStockInterface 83 | */ 84 | class StockInterface { 85 | 86 | public function stockProduct(ProductInterface $product) { 87 | // ... 88 | } 89 | } 90 | ``` 91 | 92 | ### Commenti TODO 93 | 94 | Questo tipo di commenti sono utili per indicare dei promemoria di funzionalità da correggere all'interno del codice. 95 | 96 | ### Commenti interpretati 97 | 98 | A volte i commenti vengono utilizzati dal linguaggio o dall'IDE per interpretare il nostro codice. Pensate al sistema di annotazioni utilizzato dall'ORM Doctrine o anche il `@var` utilizzato in PHP per indicare un tipo di dato. 99 | 100 | ```text 101 | /** 102 | * @return ProductInterface[] 103 | */ 104 | public function getProductsAsArray(): array { 105 | // ... 106 | } 107 | ``` 108 | 109 | ### Cattivi commenti 110 | 111 | I tipi di commenti che vedrete nelle prossime righe sono tutti esempi di commenti utilizzati in maniera errata. 112 | 113 | ### Commenti enigmatici 114 | 115 | I commenti devono chiarire i dubbi del programmatore, non aggiungerne degli altri. Evitate commenti che possono essere interpretati soltanto da chi li scrive. 116 | 117 | ### Commenti ridondanti 118 | 119 | Evitate commenti che sono inutili e meno informativi del codice stesso. I commenti devono aggiungere significato al codice, non confermare ciò che il codice già ci dice. 120 | 121 | ```text 122 | /** 123 | * Get first name 124 | * @return string 125 | */ 126 | public function getFirstName(): string { 127 | return $this->firstName; 128 | } 129 | 130 | /** 131 | * Set first name 132 | * @param string $firstName 133 | * @return void 134 | */ 135 | public function setFirstName(string $firstName): void { 136 | $this->firstName = $firstName; 137 | } 138 | ``` 139 | 140 | ### Commenti fuorvianti 141 | 142 | Bisogna stare attenti a scrivere i commenti, spesso possono essere fuorvianti e poco accurati rispetto alla reale implementazione. 143 | 144 | ```text 145 | /** 146 | * Chiude la connessione al database se è aperta 147 | * @param int $timeoutMillis 148 | * @return bool 149 | */ 150 | public function closeConnection(int $timeoutMillis): bool { 151 | if ($this->$dbConnection->isOpen()) { 152 | sleep($timeoutMillis); 153 | $this->$dbConnection->close(); 154 | return true; 155 | } 156 | 157 | return false; 158 | } 159 | ``` 160 | 161 | Questo commento, oltre ad essere ridondante, è anche fuorviante, in quanto non fa alcun riferimento al metodo `sleep` presente all'interno del metodo. Chi si ferma alla lettura del commento non potrà mai capire perché ci mette così tanto a chiudere la connessione al database. 162 | 163 | ### Commenti obbligati 164 | 165 | Non bisogna mai commentare una funzione o una variabile senza alcuna ragione o solo perché vi è una "regola" in cui tutto il codice deve essere commentato. Questo tipo di commenti non fanno altro che introdurre confusione all'interno del codice. 166 | 167 | ```text 168 | /** 169 | * Get first name 170 | * @return string 171 | */ 172 | public function getFirstName(): string { 173 | return $this->firstName; 174 | } 175 | 176 | /** 177 | * Set first name 178 | * @param string $firstName 179 | * @return void 180 | */ 181 | public function setFirstName(string $firstName): void { 182 | $this->firstName = $firstName; 183 | } 184 | ``` 185 | 186 | ### Commenti a "log" 187 | 188 | Non utilizzate i commenti solo per tenere traccia delle modifiche effettuate ad un file. Utilizzate i sistemi di controllo versione come git, che sono nati proprio con questo scopo. 189 | 190 | ### Non usate un commento al posto di una funzione o una variabile 191 | 192 | Ciò che può essere espresso sotto forma di codice, deve essere scritto attraverso il codice. 193 | 194 | Prima del refactoring: 195 | 196 | ```text 197 | // Se lo studente è maggiorenne entra dentro la condizione 198 | if ($student->getAge() > 18) { 199 | // ... 200 | } 201 | ``` 202 | 203 | Dopo il refactoring: 204 | 205 | ```text 206 | if ($student->isAdult()) { 207 | // ... 208 | } 209 | ``` 210 | 211 | ### Contrassegni di posizione 212 | 213 | Non usate i commenti per contrassegnare determinate parti di codice. 214 | 215 | ```text 216 | class Person { 217 | // ************** 218 | // * Properties * 219 | // ************** 220 | private $firstName; 221 | private $lastName; 222 | private $age; 223 | 224 | // *********** 225 | // * Methods * 226 | // *********** 227 | public function getFirstName(): string { 228 | return $this->firstName; 229 | } 230 | 231 | public function setFirstName(string $firstName): void { 232 | $this->firstName = $firstName; 233 | } 234 | } 235 | ``` 236 | 237 | ### Codice commentato 238 | 239 | Il codice commentato non fa altro che aggiungere confusione. Chi vede del codice commentato non avrà mai il coraggio di eliminarlo. Penserà che sia lì per qualche buona ragione, quando in realtà si tratta di codice risalente a qualche anno fa. 240 | 241 | ### Informazioni fuori posizione 242 | 243 | Si tratta di commenti che non descrivono il codice circostante, ma bensì funzionalità di sistema presenti altrove. Questo genere di commenti vanno evitati perché spesso non vengono aggiornati qualora vengono effettuate modifiche al sistema. 244 | 245 | ```text 246 | /** 247 | * Set port. Default port: 8082 248 | * @param int $port 249 | * @return void 250 | */ 251 | public function setPort(int $port): void { 252 | $this->port = $port; 253 | } 254 | ``` 255 | 256 | ### Commenti troppo lunghi 257 | 258 | I commenti devono essere brevi e concisi. Evitate di inserire commenti chilometrici. 259 | 260 | ### Intestazioni di funzioni 261 | 262 | Le funzioni "piccole" non hanno bisogno di commenti. Quest'ultimi, il più delle volte, sono inutili se viene utilizzato un nome "parlante" per la funzione. 263 | 264 | -------------------------------------------------------------------------------- /delimitazioni.md: -------------------------------------------------------------------------------- 1 | # Delimitazioni 2 | 3 | Spesso abbiamo la necessità di integrare nel nostro codice pacchetti e/o librerie esterne. Esistono delle tecniche che ci consentono di integrare in modo pulito questo codice esterno con il nostro. 4 | 5 | ### Usare codice esterno 6 | 7 | Chi fornisce il package o il framework punta a un'ampia applicabilità. Chi utilizza il package, invece, desidera avere delle interfacce che siano calibrate sulle proprie specifiche esigenze. Il codice di un package di terze parti è soggetto a continui cambiamenti che vanno incontro alle esigenze di un'ampia utenza. Questi cambiamenti, talvolta, potrebbero essere problematici per la nostra applicazione e potrebbero persino romperla. Perciò dobbiamo "salvaguardare" il nostro codice costruendo delle interfacce di cui abbiamo il pieno controllo. 8 | 9 | Interfaccia di cui **non** abbiamo il pieno controllo: 10 | 11 | ```text 12 | $users = [ 13 | ['name' => 'Mario', 'surname' => 'Rossi', 'age' => 35], 14 | ['name' => 'Carlo', 'surname' => 'Tropea', 'age' => 42], 15 | ['name' => 'Luca', 'surname' => 'Tozzi', 'age' => 48], 16 | ]; 17 | $userCollection = new ExternalPackage\Collection($users); 18 | $names = $userCollection->map(fn($user) => $user['name']); 19 | $userCollection->clear(); // Ops... io non volevo permettere la cancellazione :( 20 | ``` 21 | 22 | Interfaccia di cui abbiamo il pieno controllo: 23 | 24 | ```text 25 | class UserCollection { 26 | private $collection; 27 | 28 | public function __construct(array $users) { 29 | $this->collection = new ExternalPackage\Collection($users); 30 | } 31 | 32 | public function getNames(): array { 33 | if ($this->collection->hasKey('name')) { 34 | return $this->collection->map(fn($user) => $user['name']); 35 | } 36 | 37 | return []; 38 | } 39 | } 40 | 41 | $users = [ 42 | ['name' => 'Mario', 'surname' => 'Rossi', 'age' => 35], 43 | ['name' => 'Carlo', 'surname' => 'Tropea', 'age' => 42], 44 | ['name' => 'Luca', 'surname' => 'Tozzi', 'age' => 48], 45 | ]; 46 | $userCollection = new UserCollection($users); 47 | $names = $userCollection->getNames(); 48 | // Non posso effettuare la cancellazione perché la mia interfaccia non me lo consente :) 49 | ``` 50 | 51 | ### Learning Test 52 | 53 | Capire come funziona un'API esterna è difficile. Integrare il codice esterno è ancora più difficile. Invece di sperimentare le API esterne direttamente nel codice dell'applicazione, potremmo creare dei test, chiamati appunto **Learning Test**, che ci consentono di capire come funziona il codice esterno. I learning test verificano che i package esterni che usiamo funzionano come ci aspettiamo. Questo ci garantisce anche che, se il package esterno subisce dei cambiamenti con le nuove release, il nostro codice continui a funzionare. Ciò ci aiuta ad aggiornare i nostri package senza alcun timore. 54 | 55 | ### Conclusioni 56 | 57 | Un buon progetto software si deve adattare ai cambiamenti \(sia interni che esterni\). Dovremmo evitare di far conoscere al nostro codice troppi dettagli del software esterno. E' meglio dipendere da qualcosa sotto il nostro controllo che da qualcosa che non possiamo controllare. L'utilizzo dell'[Adapter Pattern](https://it.wikipedia.org/wiki/Adapter_pattern) ci aiuta ad eseguire la conversione dalla nostra interfaccia a quella fornita dal codice esterno. 58 | 59 | -------------------------------------------------------------------------------- /esempi/args.md: -------------------------------------------------------------------------------- 1 | # Args 2 | 3 | In questo esempio vedremo la costruzione della classe `Args`, la quale, data in input una stringa di formattazione e degli argomenti, e poi possibile interrogarla chiedendole il valore degli argomenti. 4 | 5 | ### **ArgumentMarshaler** 6 | 7 | ```text 8 | value; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public function set($currentArgument): void 54 | { 55 | $this->value = true; 56 | } 57 | } 58 | ``` 59 | 60 | ### **FloatArgumentMarshaler** 61 | 62 | ```text 63 | value; 81 | } 82 | 83 | return 0.0; 84 | } 85 | 86 | /** 87 | * {@inheritDoc} 88 | */ 89 | public function set($currentArgument): void 90 | { 91 | $this->value = (float)$currentArgument; 92 | } 93 | } 94 | ``` 95 | 96 | ### **IntegerArgumentMarshaler** 97 | 98 | ```text 99 | value; 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | /** 123 | * {@inheritDoc} 124 | */ 125 | public function set($currentArgument): void 126 | { 127 | $this->value = (int)$currentArgument; 128 | } 129 | } 130 | ``` 131 | 132 | ### **StringArgumentMarshaler** 133 | 134 | ```text 135 | value; 153 | } 154 | 155 | return ''; 156 | } 157 | 158 | /** 159 | * {@inheritDoc} 160 | */ 161 | public function set($currentArgument): void 162 | { 163 | $this->value = (string)$currentArgument; 164 | } 165 | } 166 | ``` 167 | 168 | ### **ArgumentMarshalerCollection** 169 | 170 | ```text 171 | array[$elementId])) { 188 | throw new ArgsException(ArgsException::UNEXPECTED_ARGUMENT, $elementId); 189 | } 190 | 191 | return $this->array[$elementId]; 192 | } 193 | 194 | /** 195 | * @param string $elementId 196 | * @param ArgumentMarshaler $am 197 | * @return void 198 | */ 199 | public function put(string $elementId, ArgumentMarshaler $am): void 200 | { 201 | $this->array[$elementId] = $am; 202 | } 203 | } 204 | ``` 205 | 206 | ### **ArgsException** 207 | 208 | ```text 209 | code = $code; 232 | $this->argumentId = $argumentId; 233 | $this->parameter = $parameter; 234 | parent::__construct($this->formatErrorMessage(), $code); 235 | } 236 | 237 | /** 238 | * @return string|null 239 | */ 240 | public function getArgumentId(): ?string 241 | { 242 | return $this->argumentId; 243 | } 244 | 245 | /** 246 | * @return string|null 247 | */ 248 | public function getParameter(): ?string 249 | { 250 | return $this->parameter; 251 | } 252 | 253 | /** 254 | * @return string 255 | */ 256 | private function formatErrorMessage(): string 257 | { 258 | switch ($this->code) { 259 | case self::INVALID_ARGUMENT_FORMAT: 260 | return sprintf('`%s` is not a valid argument format.', $this->parameter); 261 | case self::UNEXPECTED_ARGUMENT: 262 | return sprintf('Argument -%s unexpected.', $this->argumentId); 263 | case self::INVALID_ARGUMENT_NAME: 264 | return sprintf('`%s` is not a valid argument name.', $this->argumentId); 265 | default: 266 | return 'TILT: Should not get here.'; 267 | } 268 | } 269 | } 270 | ``` 271 | 272 | ### **Args** 273 | 274 | ```text 275 | marshalers = new ArgumentMarshalerCollection(); 305 | $this->args = $args; 306 | $this->parseSchema($schema); 307 | $this->parseArgumentStrings(); 308 | } 309 | 310 | /** 311 | * @param string $schema 312 | * @return void 313 | * @throws ArgsException 314 | */ 315 | private function parseSchema(string $schema): void 316 | { 317 | $elements = explode(self::SCHEMA_SEPARATOR, $schema); 318 | foreach ($elements as $element) { 319 | if (strlen($element) > 0) { 320 | $this->parseSchemaElement(trim($element)); 321 | } 322 | } 323 | } 324 | 325 | /** 326 | * @param string $element 327 | * @return void 328 | * @throws ArgsException 329 | */ 330 | private function parseSchemaElement(string $element): void 331 | { 332 | $elementId = $element[0]; 333 | $elementTail = substr($element, 1); 334 | $this->validateSchemaElementId($elementId); 335 | if (strlen($elementTail) === 0) { 336 | $this->marshalers->put($elementId, new BooleanArgumentMarshaler()); 337 | } elseif ($elementTail === StringArgumentMarshaler::SYMBOL) { 338 | $this->marshalers->put($elementId, new StringArgumentMarshaler()); 339 | } elseif ($elementTail === IntegerArgumentMarshaler::SYMBOL) { 340 | $this->marshalers->put($elementId, new IntegerArgumentMarshaler()); 341 | } elseif ($elementTail === FloatArgumentMarshaler::SYMBOL) { 342 | $this->marshalers->put($elementId, new FloatArgumentMarshaler()); 343 | } else { 344 | throw new ArgsException(ArgsException::INVALID_ARGUMENT_FORMAT, $elementId, $elementTail); 345 | } 346 | } 347 | 348 | /** 349 | * @param string $elementId 350 | * @return void 351 | * @throws ArgsException 352 | */ 353 | private function validateSchemaElementId(string $elementId): void 354 | { 355 | if (!ctype_alpha($elementId)) { 356 | throw new ArgsException(ArgsException::INVALID_ARGUMENT_NAME, $elementId); 357 | } 358 | } 359 | 360 | /** 361 | * @return void 362 | * @throws ArgsException 363 | */ 364 | private function parseArgumentStrings(): void 365 | { 366 | foreach ($this->args as $arg) { 367 | if (substr($arg, 0, 1) === '-') { 368 | $this->parseArgumentCharacters(substr($arg, 1)); 369 | } 370 | } 371 | } 372 | 373 | /** 374 | * @param string $argChars 375 | * @return void 376 | * @throws ArgsException 377 | */ 378 | private function parseArgumentCharacters(string $argChars): void 379 | { 380 | for ($i = 0; $i < strlen($argChars); $i++) { 381 | $this->parseArgumentCharacter($argChars[$i]); 382 | } 383 | } 384 | 385 | /** 386 | * @param string $argChar 387 | * @return void 388 | * @throws ArgsException 389 | */ 390 | private function parseArgumentCharacter(string $argChar): void 391 | { 392 | $am = $this->marshalers->get($argChar); 393 | $am->set(next($this->args)); 394 | $this->argsFound[] = $argChar; 395 | } 396 | 397 | /** 398 | * @param string $arg 399 | * @return bool 400 | */ 401 | public function has(string $arg): bool 402 | { 403 | return in_array($arg, $this->argsFound); 404 | } 405 | 406 | /** 407 | * @param string $arg 408 | * @return bool 409 | * @throws ArgsException 410 | */ 411 | public function getBoolean(string $arg): bool 412 | { 413 | return BooleanArgumentMarshaler::getValue($this->marshalers->get($arg)); 414 | } 415 | 416 | /** 417 | * @param string $arg 418 | * @return string 419 | * @throws ArgsException 420 | */ 421 | public function getString(string $arg): string 422 | { 423 | return StringArgumentMarshaler::getValue($this->marshalers->get($arg)); 424 | } 425 | 426 | /** 427 | * @param string $arg 428 | * @return int 429 | * @throws ArgsException 430 | */ 431 | public function getInteger(string $arg): int 432 | { 433 | return IntegerArgumentMarshaler::getValue($this->marshalers->get($arg)); 434 | } 435 | 436 | /** 437 | * @param string $arg 438 | * @return float 439 | * @throws ArgsException 440 | */ 441 | public function getFloat(string $arg): float 442 | { 443 | return FloatArgumentMarshaler::getValue($this->marshalers->get($arg)); 444 | } 445 | } 446 | ``` 447 | 448 | ### **Index** 449 | 450 | ```text 451 | getBoolean('l')); 458 | var_dump($args->getInteger('p')); 459 | var_dump($args->getString('d')); 460 | var_dump($args->getFloat('x')); 461 | } catch (ArgsException $e) { 462 | var_dump($e->getMessage()); 463 | } 464 | ``` 465 | 466 | 467 | 468 | -------------------------------------------------------------------------------- /esempi/comparisoncompactor.md: -------------------------------------------------------------------------------- 1 | # ComparisonCompactor 2 | 3 | In questo esempio andremo a realizzare e rifattorizzare un comparatore di stringhe. Date due stringhe differenti, come **abc** e **abbc**, mostra la differenza generando una stringa del tipo: **"expected: {ab\[\]c} but was: {ab\[b\]c}"**. 4 | 5 | ### Substring 6 | 7 | ```text 8 | _contextLength = $contextLength; 62 | $this->_expected = $expected; 63 | $this->_actual = $actual; 64 | } 65 | 66 | /** 67 | * @param string|null $message 68 | * @return string 69 | */ 70 | public function compact(string $message = null): string 71 | { 72 | if ($this->_expected === null || $this->_actual === null || $this->areStringsEqual()) { 73 | return trim(sprintf('%s expected: {%s} but was: {%s}', $message, $this->_expected, $this->_actual)); 74 | } 75 | 76 | $this->findCommonPrefix(); 77 | $this->findCommonSuffix(); 78 | $expected = $this->compactString($this->_expected); 79 | $actual = $this->compactString($this->_actual); 80 | return trim(sprintf('%s expected: {%s} but was: {%s}', $message, $expected, $actual)); 81 | } 82 | 83 | /** 84 | * @param string $source 85 | * @return string 86 | */ 87 | private function compactString(string $source): string 88 | { 89 | $result = 90 | self::DELTA_START . 91 | substring($source, $this->_prefix, strlen($source) - $this->_suffix + 1) . 92 | self::DELTA_END; 93 | 94 | if ($this->_prefix > 0) { 95 | $result = $this->computeCommonPrefix() . $result; 96 | } 97 | 98 | if ($this->_suffix > 0) { 99 | $result = $result . $this->computeCommonSuffix(); 100 | } 101 | 102 | return $result; 103 | } 104 | 105 | /** 106 | * @return void 107 | */ 108 | private function findCommonPrefix(): void 109 | { 110 | $this->_prefix = 0; 111 | $end = min(strlen($this->_expected), strlen($this->_actual)); 112 | for (; $this->_prefix < $end; $this->_prefix++) { 113 | if ($this->_expected[$this->_prefix] !== $this->_actual[$this->_prefix]) { 114 | break; 115 | } 116 | } 117 | } 118 | 119 | /** 120 | * @return void 121 | */ 122 | private function findCommonSuffix(): void 123 | { 124 | $expectedSuffix = strlen($this->_expected) - 1; 125 | $actualSuffix = strlen($this->_actual) - 1; 126 | for (; $actualSuffix >= $this->_prefix && $expectedSuffix >= $this->_prefix; $actualSuffix--, $expectedSuffix--) { 127 | if ($this->_expected[$expectedSuffix] !== $this->_actual[$actualSuffix]) { 128 | break; 129 | } 130 | } 131 | $this->_suffix = strlen($this->_expected) - $expectedSuffix; 132 | } 133 | 134 | /** 135 | * @return string 136 | */ 137 | private function computeCommonPrefix(): string 138 | { 139 | return 140 | ($this->_prefix > $this->_contextLength ? self::ELLIPSIS : '') . 141 | (substring($this->_expected, max(0, $this->_prefix - $this->_contextLength), $this->_prefix)); 142 | } 143 | 144 | /** 145 | * @return string 146 | */ 147 | private function computeCommonSuffix(): string 148 | { 149 | $end = min(strlen($this->_expected) - $this->_suffix + 1 + $this->_contextLength, strlen($this->_expected)); 150 | return 151 | (substring($this->_expected, strlen($this->_expected) - $this->_suffix + 1, $end)) . 152 | (strlen($this->_expected) - $this->_suffix + 1 < strlen($this->_expected) - $this->_contextLength ? self::ELLIPSIS : ''); 153 | } 154 | 155 | /** 156 | * @return bool 157 | */ 158 | private function areStringsEqual(): bool 159 | { 160 | return $this->_expected === $this->_actual; 161 | } 162 | } 163 | ``` 164 | 165 | ### ComparisonCompactor \(dopo il refactoring\) 166 | 167 | ```text 168 | contextLength = $contextLength; 201 | $this->expected = $expected; 202 | $this->actual = $actual; 203 | } 204 | 205 | /** 206 | * @param string|null $message 207 | * @return string 208 | */ 209 | public function formatCompactedComparison(string $message = null): string 210 | { 211 | $compactExpected = $this->expected; 212 | $compactActual = $this->actual; 213 | 214 | if ($this->shouldBeCompacted()) { 215 | $this->findCommonPrefixAndSuffix(); 216 | $compactExpected = $this->compact($this->expected); 217 | $compactActual = $this->compact($this->actual); 218 | } 219 | 220 | return trim(sprintf('%s expected: {%s} but was: {%s}', $message, $compactExpected, $compactActual)); 221 | } 222 | 223 | /** 224 | * @return bool 225 | */ 226 | private function shouldBeCompacted(): bool 227 | { 228 | return !$this->shouldNotBeCompacted(); 229 | } 230 | 231 | /** 232 | * @return bool 233 | */ 234 | private function shouldNotBeCompacted(): bool 235 | { 236 | return $this->expected === null || $this->actual === null || $this->expected === $this->actual; 237 | } 238 | 239 | /** 240 | * @return void 241 | */ 242 | private function findCommonPrefixAndSuffix(): void 243 | { 244 | $this->findCommonPrefix(); 245 | $this->suffixLength = 0; 246 | for (; !$this->suffixOverlapsPrefix(); $this->suffixLength++) { 247 | if ($this->charFromEnd($this->expected) !== $this->charFromEnd($this->actual)) { 248 | break; 249 | } 250 | } 251 | } 252 | 253 | /** 254 | * @return void 255 | */ 256 | private function findCommonPrefix(): void 257 | { 258 | $this->prefixLength = 0; 259 | $end = min(strlen($this->expected), strlen($this->actual)); 260 | for (; $this->prefixLength < $end; $this->prefixLength++) { 261 | if ($this->expected[$this->prefixLength] !== $this->actual[$this->prefixLength]) { 262 | break; 263 | } 264 | } 265 | } 266 | 267 | /** 268 | * @return bool 269 | */ 270 | private function suffixOverlapsPrefix(): bool 271 | { 272 | return ( 273 | strlen($this->actual) - $this->suffixLength <= $this->prefixLength || 274 | strlen($this->expected) - $this->suffixLength <= $this->prefixLength 275 | ); 276 | } 277 | 278 | /** 279 | * @param string $s 280 | * @return string 281 | */ 282 | private function charFromEnd(string $s): string 283 | { 284 | return $s[strlen($s) - $this->suffixLength - 1]; 285 | } 286 | 287 | /** 288 | * @param string $s 289 | * @return string 290 | */ 291 | private function compact(string $s): string 292 | { 293 | return ( 294 | $this->startEllipsis() . 295 | $this->startContext() . 296 | self::DELTA_START . 297 | $this->delta($s) . 298 | self::DELTA_END . 299 | $this->endContext() . 300 | $this->endEllipsis() 301 | ); 302 | } 303 | 304 | /** 305 | * @return string 306 | */ 307 | private function startEllipsis(): string 308 | { 309 | return $this->prefixLength > $this->contextLength ? self::ELLIPSIS : ''; 310 | } 311 | 312 | /** 313 | * @return string 314 | */ 315 | private function startContext(): string 316 | { 317 | $contextStart = max(0, $this->prefixLength - $this->contextLength); 318 | $contextEnd = $this->prefixLength; 319 | return substring($this->expected, $contextStart, $contextEnd); 320 | } 321 | 322 | /** 323 | * @param string $s 324 | * @return string 325 | */ 326 | private function delta(string $s): string 327 | { 328 | $deltaStart = $this->prefixLength; 329 | $deltaEnd = strlen($s) - $this->suffixLength; 330 | return substring($s, $deltaStart, $deltaEnd); 331 | } 332 | 333 | /** 334 | * @return string 335 | */ 336 | private function endContext(): string 337 | { 338 | $contextStart = strlen($this->expected) - $this->suffixLength; 339 | $contextEnd = min($contextStart + $this->contextLength, strlen($this->expected)); 340 | return substring($this->expected, $contextStart, $contextEnd); 341 | } 342 | 343 | /** 344 | * @return string 345 | */ 346 | private function endEllipsis(): string 347 | { 348 | return $this->suffixLength > $this->contextLength ? self::ELLIPSIS : ''; 349 | } 350 | } 351 | ``` 352 | 353 | ### Index 354 | 355 | ```text 356 | Original: ' . PHP_EOL . $comparisonOriginal->compact())); 366 | 367 | print_r(nl2br(PHP_EOL . PHP_EOL)); 368 | 369 | $comparisonFinal = new ComparisonCompactorFinal($contextLength, $expected, $actual); 370 | print_r(nl2br(' Final: ' . PHP_EOL . $comparisonFinal->formatCompactedComparison())); 371 | ``` 372 | 373 | 374 | 375 | -------------------------------------------------------------------------------- /esempi/primegenerator.md: -------------------------------------------------------------------------------- 1 | # PrimeGenerator 2 | 3 | In questo esempio andremo a scrivere una classe che genera dei numeri primi fino a un massimo specificato dall'utente. 4 | 5 | ### Prima del refactoring 6 | 7 | ```text 8 | = 2) { 31 | // Dichiarazioni 32 | $s = $maxValue + 1; // Dimensione dell'array 33 | $f = []; 34 | 35 | // Inizializza l'array a true 36 | for ($i = 0; $i < $s; $i++) { 37 | $f[$i] = true; 38 | } 39 | 40 | // Elimina i non-primi 41 | $f[0] = $f[1] = false; 42 | 43 | // Setaccio 44 | for ($i = 2; $i < sqrt($s) + 1; $i++) { 45 | // Se i è primo, elimina i suoi multipli 46 | if ($f[$i]) { 47 | for ($j = 2 * $i; $j < $s; $j += $i) { 48 | $f[$j] = false; // Un multiplo non può essere primo 49 | } 50 | } 51 | } 52 | 53 | // Quanti primi ci sono? 54 | $count = 0; 55 | for ($i = 0; $i < $s; $i++) { 56 | if ($f[$i]) { 57 | $count++; 58 | } 59 | } 60 | 61 | // Sposto i primi nel risultato 62 | $primes = []; 63 | for ($i = 0, $j = 0; $i < $s; $i++) { 64 | // Se è primo 65 | if ($f[$i]) { 66 | $primes[$j++] = $i; 67 | } 68 | } 69 | 70 | return $primes; 71 | } else { // $maxValue < 2 72 | return []; 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | ### Dopo il refactoring 79 | 80 | ```text 81 | rowsPerPage = $rowsPerPage; 106 | $this->columnsPerPage = $columnsPerPage; 107 | $this->numbersPerPage = $rowsPerPage * $columnsPerPage; 108 | $this->pageHeader = $pageHeader; 109 | $this->data = $data; 110 | } 111 | 112 | public function print(): void 113 | { 114 | $pageNumber = 1; 115 | for ($firstIndexOnPage = 0; $firstIndexOnPage < count($this->data); $firstIndexOnPage += $this->numbersPerPage) { 116 | $lastIndexOnPage = min(($firstIndexOnPage + $this->numbersPerPage) - 1, count($this->data) - 1); 117 | $this->printPageHeader($pageNumber); 118 | $this->printPage($firstIndexOnPage, $lastIndexOnPage); 119 | echo nl2br(PHP_EOL); 120 | $pageNumber++; 121 | } 122 | } 123 | 124 | private function printPageHeader(int $pageNumber): void 125 | { 126 | echo nl2br($this->pageHeader . ' --- Page ' . $pageNumber . PHP_EOL); 127 | echo nl2br(PHP_EOL); 128 | } 129 | 130 | private function printPage(int $firstIndexOnPage, int $lastIndexOnPage): void 131 | { 132 | $firstIndexOfLastRowOnPage = ($firstIndexOnPage + $this->rowsPerPage) - 1; 133 | for ($firstIndexInRow = $firstIndexOnPage; $firstIndexInRow <= $firstIndexOfLastRowOnPage; $firstIndexInRow++) { 134 | $this->printRow($firstIndexInRow, $lastIndexOnPage); 135 | echo nl2br(PHP_EOL); 136 | } 137 | } 138 | 139 | private function printRow(int $firstIndexInRow, int $lastIndexOnPage): void 140 | { 141 | for ($column = 0; $column < $this->columnsPerPage; $column++) { 142 | $index = $firstIndexInRow + ($column * $this->rowsPerPage); 143 | if ($index <= $lastIndexOnPage) { 144 | echo sprintf('%10d', $this->data[$index]); 145 | } 146 | } 147 | } 148 | } 149 | ``` 150 | 151 | ### **PrimePrinter** 152 | 153 | ```text 154 | rowColumnPagePrinter = new RowColumnPagePrinter( 169 | self::ROWS_PER_PAGE, 170 | self::COLUMNS_PER_PAGE, 171 | 'The First ' . $numberOfPrimes . ' Prime Numbers', 172 | $primes 173 | ); 174 | } 175 | 176 | public function __invoke(): void 177 | { 178 | $this->rowColumnPagePrinter->print(); 179 | } 180 | } 181 | ``` 182 | 183 | ### **Index** 184 | 185 | ```text 186 | assertTrue($message, !$condition); 85 | } 86 | } 87 | ``` 88 | 89 | ### Formattazione orizzontale 90 | 91 | Per formattazione orizzontale si intendono tutte quelle regole che vengono applicate per rendere chiara la lettura del codice da sinistra a destra. 92 | 93 | ### Quanto può essere lunga una riga 94 | 95 | In genere una riga in un file di codice sorgente non deve superare gli 80 caratteri. 96 | 97 | ### Allineamento orizzontale 98 | 99 | Evitate l'utilizzo dell'allineamento orizzontale, in quanto distoglie spesso lo sguardo da informazioni importanti quali il tipo \(nel caso delle dichiarazioni\) e l'operatore utilizzato \(nel caso di operazioni di assegnamento\). 100 | 101 | ```text 102 | class User implements JsonSerializable { 103 | private $firstName; 104 | private $lastName; 105 | private $age; 106 | 107 | public function jsonSerialize(): array { 108 | return [ 109 | 'firstName' => $this->firstName, 110 | 'lastName' => $this->lastName, 111 | 'age' => $this->age, 112 | ]; 113 | } 114 | } 115 | ``` 116 | 117 | Diventa: 118 | 119 | ```text 120 | class User implements JsonSerializable { 121 | private $firstName; 122 | private $lastName; 123 | private $age; 124 | 125 | public function jsonSerialize(): array { 126 | return [ 127 | 'firstName' => $this->firstName, 128 | 'lastName' => $this->lastName, 129 | 'age' => $this->age, 130 | ]; 131 | } 132 | } 133 | ``` 134 | 135 | -------------------------------------------------------------------------------- /funzioni.md: -------------------------------------------------------------------------------- 1 | # Funzioni 2 | 3 | Le funzioni rappresentano le azioni che è possibile compiere all'interno del nostro software. Per questo motivo è importante che siano chiare e semplici da leggere. Come fare? Scopriamolo insieme! 4 | 5 | ### Che sia Piccola 6 | 7 | La prima regola delle funzioni è che devono essere piccole. In generale una funzione non dovrebbe superare le 20 righe di codice. 8 | 9 | ### Blocchi e indentazione 10 | 11 | I blocchi delle istruzioni `if`, `else`, `for` e così via, devono essere di una sola riga. Probabilmente tale riga dovrà contenere una chiamata a funzione. In questa maniera il nostro codice non solo resta compatto, ma sarà anche più chiaro, perché la funzione richiamata avrà un nome descrittivo. Ciò consente anche di non avere grandi strutture annidate. Il livello di indentazione di una funzione non dovrebbe essere superiore a uno o due. 12 | 13 | ### Che faccia una cosa sola 14 | 15 | Cosa significa "fare una cosa sola"? Prendiamo per esempio questa funzione: 16 | 17 | ```text 18 | public function renderPageWithSetupsAndTeardowns() { 19 | if ($this->isTestPage($this->pageData)) { 20 | $this->includeSetupAndTeardown($this->pageData, $this->isSuite); 21 | } 22 | 23 | return $this->pageData; 24 | } 25 | ``` 26 | 27 | In ordine questa funzione fa tre cose: 28 | 29 | - Determina se è una pagina di test 30 | - In caso affermativo, include attacchi e chiusure 31 | - Esegue il rendering della pagina HTML 32 | 33 | Apparentemente questa funzione fa tre cose, ma notate come questi passi si trovano ad un solo livello di astrazione da quello che è il nome della funzione. In generale, una funzione fa una cosa sola quando i passi della funzione si trovano ad un solo livello di astrazione da quello che è il nome scelto per la funzione. Un altro modo per capire se una funzione sta facendo più di una cosa, è scoprire se riuscite ad estrarre un'altra funzione con un nome che non è semplicemente una riaffermazione della sua implementazione. 34 | 35 | ### Sezioni all'interno delle funzioni 36 | 37 | Le funzioni che fanno più di una cosa spesso possono essere suddivise in tre sezioni: _dichiarazioni_, _inizializzazioni_ e _setaccio_. Se la funzione a cui stai lavorando presenta queste tre sezioni, vuol dire che essa non fa una cosa sola. 38 | 39 | ### La regola dei passi 40 | 41 | Le funzioni devono essere lette come se fossero dei paragrafi, ognuno dei quali descrive il livello di astrazione corrente e fa riferimento ai successivi paragrafi \(ovvero alle funzioni\) posti ad un livello di astrazione successivo. 42 | 43 | ### Istruzioni switch 44 | 45 | Le istruzioni `switch` sono problematiche, in quanto le funzioni che le contengono non solo fanno più di una cosa, ma spesso violano anche i principi SOLID. 46 | 47 | ```text 48 | public function calculatePay(Employee $employee): float { 49 | switch($employee->getType()) { 50 | case Employee::COMMISSIONED: 51 | return $this->calculateCommissionedPay($employee); 52 | case Employee::HOURLY: 53 | return $this->calculateHourlyPay($employee); 54 | case Employee::SALARIED: 55 | return $this->calculateSalariedPay($employee); 56 | default: 57 | throw new InvalidEmployeeType($employee->getType()); 58 | } 59 | } 60 | ``` 61 | 62 | Questa funzione viola sia il principio di singola responsabilità \(SRP\), che il principio Open Closed \(OCP\) in quanto ogniqualvolta che si aggiungerà un nuovo tipo, si dovrà modificare la funzione. La soluzione in questi casi è quella di usare il polimorfismo + una classe factory che si occupa di istanziare oggetti a seconda del tipo. Le istruzioni `switch` sono infatti tollerate solo se vengono usate per creare oggetti polimorfici. 63 | 64 | ```text 65 | interface Employee { 66 | public function isPayday(): bool; 67 | public function calculatePay(): float; 68 | public function deliverPay(Money $pay): void; 69 | } 70 | 71 | interface EmployeeFactory { 72 | public function make(EmployeeRecord $record): Employee; 73 | } 74 | 75 | class EmployeeFactoryImpl implements EmployeeFactory { 76 | 77 | public function make(EmployeeRecord $record): Employee { 78 | switch($record->getType()) { 79 | case EmployeeRecord::COMMISSIONED: 80 | return new CommissionedEmployee(); 81 | case EmployeeRecord::HOURLY: 82 | return new HourlyEmployee(); 83 | case EmployeeRecord::SALARIED: 84 | return new SalariedEmployee(); 85 | default: 86 | throw new InvalidEmployee($record->getType()); 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | ### Usate nomi descrittivi 93 | 94 | Il nome della funzione deve chiarire subito ciò che la funzione fa. Più piccola sarà la funzione, più facile sarà scegliere un nome descrittivo. Inoltre è sempre meglio dare un nome lungo e descrittivo, piuttosto che un nome corto ed enigmatico preceduto da un lungo commento. 95 | 96 | ### Argomenti di funzione 97 | 98 | Una funzione non dovrebbe avere più di tre argomenti. Gli argomenti in una funzione, non solo la rendono difficile da leggere, ma generano anche difficoltà nello scrivere tutti i casi di test. 99 | 100 | ### Funzioni ad un solo argomento \(monadica\) 101 | 102 | Le funzioni con un solo argomento \(monadica\) vengono usate o per fare una domanda relativa a tale argomento: `isFileExists("test.txt")` o per operare su tale argomento: `fileOpen("test.txt")`. Un caso eccezionale sono gli eventi, dove a fronte di un argomento di input non c'è alcun valore restituito. 103 | 104 | ### Evitate i flag come argomenti 105 | 106 | I flag come argomenti sono da evitare, in quanto è sintomo che la funzione non fa una cosa sola. Fai X se il flag è `true` altrimenti fai Y. 107 | 108 | ### Funzioni a due argomenti \(diadica\) 109 | 110 | Le funzioni a due argomenti \(diadica\) sono più difficili da leggere, per questo motivo vanno preferibilmente utilizzate solo quando c'è coerenza tra i due argomenti: `new Point(12, 23)`. In tutti gli altri casi si consiglia di trasformare le funzioni diadiche in funzioni monadiche. 111 | 112 | Funzione diadica: 113 | 114 | ```text 115 | class Example { 116 | 117 | public function writeField(StreamInterface $outputStream, string $name) { 118 | // ... 119 | } 120 | } 121 | ``` 122 | 123 | Funzione monadica: 124 | 125 | ```text 126 | class Example { 127 | 128 | private $outputStream; 129 | 130 | public function __construct(StreamInterface $outputStream) { 131 | $this->outputStream = $outputStream; 132 | } 133 | 134 | public function writeField(string $name) { 135 | // ... 136 | } 137 | } 138 | ``` 139 | 140 | ### Funzioni a tre argomenti \(triadica\) 141 | 142 | Le funzioni a tre argomenti \(triadica\) sono da evitare, in quanto rendono molto più difficile sia leggere il codice che testarlo. Utilizzarle solo in casi eccezionali. 143 | 144 | ### Oggetti usati come argomenti 145 | 146 | Se una funzione richiede più di due / tre argomenti, è probabile che questi argomenti devono essere inseriti all'interno di una classe separata. 147 | 148 | Funzione a tre argomenti: 149 | 150 | ```text 151 | public function makeCircle(double $x, double $y, double $radius) 152 | ``` 153 | 154 | Funzione a due argomenti: 155 | 156 | ```text 157 | public function makeCircle(Point $p, double $radius) 158 | ``` 159 | 160 | ### Verbi e parole chiave 161 | 162 | La scelta del nome della funzione è fondamentale per chiarire sia lo scopo della funzione e sia lo scopo e l'ordine degli argomenti. Per tale ragione occorre trovare una buona accoppiata verbo/nome che tenga conto anche degli argomenti presenti nella funzione. 163 | 164 | Ad esempio questa funzione: 165 | 166 | ```text 167 | public function assertEquals($expected, $actual) 168 | ``` 169 | 170 | Potrebbe essere rinominata in: 171 | 172 | ```text 173 | public function assertExpectedEqualsActual($expected, $actual) 174 | ``` 175 | 176 | ### Niente effetti collaterali 177 | 178 | Le funzioni non devono avere effetti collaterali. Se una funzione dice di fare X e poi fa anche Y, questo, non solo viola la regola di "fare una cosa sola", ma potrebbe anche portare a comportamenti indesiderati. 179 | 180 | ### Argomenti di output 181 | 182 | Evitate gli argomenti di output, in quanto sono brutti e difficili da leggere. Se la funzione deve modificare lo stato, lo deve fare per l'oggetto a cui appartiene. 183 | 184 | Funzione da evitare: 185 | 186 | ```text 187 | public function appendFooter(string &$pageHtml) 188 | ``` 189 | 190 | Funzione corretta: 191 | 192 | ```text 193 | class Page { 194 | private $html; 195 | 196 | public function appendFooter() { 197 | // ... 198 | } 199 | } 200 | 201 | $page = new Page(); 202 | $page->appendFooter(); 203 | ``` 204 | 205 | ### Separate i comandi dalle richieste 206 | 207 | Le funzioni devono _fare_ qualcosa oppure _rispondere_ a qualcosa, non entrambe le cose. La funzione deve o modificare lo stato di un oggetto oppure restituire un informazione su tale oggetto. Se fa entrambe le cose, vuol dire che la funzione ha più di una responsabilità. 208 | 209 | ```text 210 | public function set(string $attributeName, string $value): bool { 211 | // ... 212 | } 213 | 214 | if ($this->set('username', 'mirko123')) { 215 | // ... 216 | } 217 | ``` 218 | 219 | Questa funzione imposta il valore all'attributo e restituisce `true` se ha successo \(e quindi l'attributo esiste\), altrimenti ritorna `false`. La funzione è poco chiara, per sistemarla occorre separare il comando dalla richiesta: 220 | 221 | ```text 222 | public function isAttributeExists(string $attributeName): bool { 223 | // ... 224 | } 225 | 226 | public function setAttribute(string $attributeName, string $value): void { 227 | // ... 228 | } 229 | 230 | if ($this->isAttributeExists('username')) { 231 | $this->setAttribute('username', 'mirko123'); 232 | } 233 | ``` 234 | 235 | ### Scegliete le eccezioni invece dei codici d'errore 236 | 237 | I codici d'errore sono da evitare perché inducono a creare istruzioni `if` annidate. Al loro posto è meglio utilizzare le eccezioni. 238 | 239 | ```text 240 | if ($this->userRepository->delete($user) === UserRecord::STATUS_OK) { 241 | if ($this->mailer->send($email) === MailerRecord::STATUS_OK) { 242 | print_r('Email inviata con successo.'); 243 | } else { 244 | print_r('Non è stato possibile inviare l`email.'); 245 | } 246 | } else { 247 | print_r('Non è stato possibile eliminare l`utente.'); 248 | } 249 | ``` 250 | 251 | Diventa: 252 | 253 | ```text 254 | public function deleteUser(User $user) { 255 | try { 256 | $this->userRepository->delete($user); 257 | $this->mailer->send($this->email); 258 | } catch(Exception $e) { 259 | print_r($e->getMessage()); 260 | } 261 | } 262 | ``` 263 | 264 | ### Estraete i blocchi try / catch 265 | 266 | I blocchi try / catch sono brutti da vedere e spesso creano confusione. Pertanto è meglio estrarli e inserirli all'interno di funzioni a sé stanti. 267 | 268 | L'esempio sopra diventa: 269 | 270 | ```text 271 | public function deleteUser(User $user) { 272 | try { 273 | $this->deleteUserAndSendEmail($user); 274 | } catch(Exception $e) { 275 | print_r($e->getMessage()); 276 | } 277 | } 278 | 279 | private function deleteUserAndSendEmail(User $user) { 280 | $this->userRepository->delete($user); 281 | $this->mailer->send($this->email); 282 | } 283 | ``` 284 | 285 | ### Il principio DRY 286 | 287 | Seguite il principio DRY \(_Don't Repeat Yourself_\) per evitare duplicazioni nel codice. 288 | 289 | -------------------------------------------------------------------------------- /gestione-degli-errori.md: -------------------------------------------------------------------------------- 1 | # Gestione degli errori 2 | 3 | La gestione degli errori è certamente importante quando sviluppiamo, ma spesso essa sembra quasi "dominare" il nostro codice, tanto da offuscarlo e renderlo illegibile. 4 | 5 | ### Usate le eccezioni al posto dei codici d'errore 6 | 7 | L'utilizzo delle eccezioni al posto dei codici d'errore migliora di gran lunga la leggibilità del nostro codice. 8 | 9 | Utilizzo dei codici d'errore: 10 | 11 | ```text 12 | if ($this->userRepository->delete($user) === UserRecord::STATUS_OK) { 13 | if ($this->mailer->send($email) === MailerRecord::STATUS_OK) { 14 | print_r('Email inviata con successo.'); 15 | } else { 16 | print_r('Non è stato possibile inviare l`email.'); 17 | } 18 | } else { 19 | print_r('Non è stato possibile eliminare l`utente.'); 20 | } 21 | ``` 22 | 23 | Utilizzo delle eccezioni: 24 | 25 | ```text 26 | try { 27 | $this->userRepository->delete($user); 28 | $this->mailer->send($this->email); 29 | } catch(Exception $e) { 30 | print_r($e->getMessage()); 31 | } 32 | ``` 33 | 34 | ### Definite le classi per le eccezioni in base alle esigenze 35 | 36 | Quando definiamo una classe per un'eccezione, dobbiamo pensare al modo in cui essa può essere cachata e utilizzata. Spesso un'unica classe per eccezioni è sufficiente per distinguere una determinata area/situazione nel codice. 37 | 38 | Utilizzo superfluo delle classi per le eccezioni: 39 | 40 | ```text 41 | if (empty($product->getName())) { 42 | throw new InvalidProductNameException(); 43 | } 44 | 45 | if ($product->getPrice() < 0) { 46 | throw new InvalidProductPriceException(); 47 | } 48 | 49 | if (empty($product->getUrlKey())) { 50 | throw new InvalidProductUrlKeyException(); 51 | } 52 | ``` 53 | 54 | Utilizzo corretto delle classi per le eccezioni: 55 | 56 | ```text 57 | if ($product->isValid() === false) { 58 | throw new InvalidProductException($product); 59 | } 60 | ``` 61 | 62 | ### Definite il flusso "normale" 63 | 64 | Solitamente, quando si verifica un'eccezione, la gestiamo loggando l'errore, mostrando eventualmente un messaggio all'utente e dopodichè proseguiamo con la normale esecuzione del codice. Tuttavia ci sono casi in cui vogliamo gestire l'eccezione in maniera "speciale": 65 | 66 | ```text 67 | try { 68 | $expenses = $expenseReportDAO->getMeals($employee->getId()); 69 | $total += $expenses->getTotal(); 70 | } catch (MealExpensesNotFound $e) { 71 | $total += $this->getMealPerDiem(); 72 | } 73 | 74 | class ExpenseReportDAO { 75 | public function getMeals(int $employeeId): MealExpenses { 76 | // recupera i pasti consumati tramite l'id del dipendente 77 | if (empty($meals)) { 78 | throw new MealExpensesNotFound(); 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | In questo caso, se il pasto viene consumato, diviene parte del totale, altrimenti il dipendente riceve una somma per tale giorno. Questo codice diventa più semplice da leggere, se gestiamo il caso speciale presente nel blocco `catch` attraverso una classe a parte \(SPECIAL CASE PATTERN\). 85 | 86 | ```text 87 | $expenses = $expenseReportDAO->getMeals($employee->getId()); 88 | $total += $expenses->getTotal(); 89 | 90 | class ExpenseReportDAO { 91 | public function getMeals(int $employeeId): MealExpenses { 92 | // recupera i pasti consumati tramite l'id del dipendente 93 | if (empty($meals)) { 94 | return new PerDiemMealExpenses($employee); 95 | } 96 | } 97 | } 98 | 99 | class PerDiemMealExpenses implements MealExpenses { 100 | public function getTotal(): float { 101 | // restituisce il totale per giorno 102 | } 103 | } 104 | ``` 105 | 106 | ### Non restituite null 107 | 108 | Non restituite `null` se qualcosa va storto, piuttosto lanciate un'eccezione o restituite un oggetto Special Case come mostrato nell'esempio precedente. L'utilizzo di `null` è sconsigliato perché aumenta il carico di lavoro del chiamante che deve verificare se il valore restituito è `null` o meno, portando di conseguenza ad avere condizioni `if` annidate. 109 | 110 | Prima del refactoring: 111 | 112 | ```text 113 | $employees = $employeeRepository->getEmployees(); 114 | $totalPay = 0; 115 | 116 | if ($employees !== null) { 117 | foreach ($employees as $employee) { 118 | $totalPay += $employee->getPay(); 119 | } 120 | } 121 | ``` 122 | 123 | Dopo il refactoring: 124 | 125 | ```text 126 | $employees = $employeeRepository->getEmployees(); 127 | $totalPay = 0; 128 | 129 | foreach ($employees as $employee) { 130 | $totalPay += $employee->getPay(); 131 | } 132 | 133 | class EmployeeRepository { 134 | public function getEmployees() { 135 | // recupera la lista dei dipendenti 136 | if (empty($employees)) { 137 | return []; 138 | } 139 | } 140 | } 141 | ``` 142 | 143 | ### Non passate null 144 | 145 | E' sconsigliato passare `null` come argomento \(salvo casi particolari\), perché questo obbliga al metodo chiamato di verificare il caso in cui l'argomento è `null` e il caso in cui non lo è. 146 | 147 | -------------------------------------------------------------------------------- /nomi-significativi.md: -------------------------------------------------------------------------------- 1 | # Nomi significativi 2 | 3 | > There are only two hard things in Computer Science: cache invalidation and naming things. 4 | 5 | I nomi sono dappertutto nel software \(variabili, funzioni, argomenti, classi, istanze, file, cartelle...\), per questo motivo è molto importante nominare bene le cose. Ecco delle semplici regole per scegliere dei buoni nomi. 6 | 7 | ## Usate nomi "parlanti" 8 | 9 | I nomi devono essere parlanti. Già dal nome deve essere chiaro il perchè esiste, cosa fa e come viene utilizzato ciò che state nominando. 10 | 11 | Esempio di cattivo nome: 12 | 13 | ```text 14 | $d = 12; 15 | $list1 = []; 16 | $a = true; 17 | ``` 18 | 19 | Esempio di buon nome: 20 | 21 | ```text 22 | $daysSinceCreation = 12; 23 | $flaggedCells = []; 24 | $isFlagged = true; 25 | ``` 26 | 27 | ## Evitate la disinformazione 28 | 29 | I nomi non devono essere ambigui e disinformativi. Se uso `$accountList` e non si tratta di una lista, ma di un gruppo di accounts, allora è meglio utilizzare `$accountGroup`. 30 | 31 | ## Adottate distinzioni sensate 32 | 33 | Usare due nomi uguali che si distinguono solo per un valore posto alla fine del nome \(Es. `$customer1` e `$customer2`\) non è sensato. Piuttosto cercate di dare un nome che ben distingua le due cose \(Es. `$buyer` e `$seller`\). Anche nel nominare le classi si commette questo errore. Pensate a quante volte vi è capitato di trovare due classi che si chiamavano rispettivamente `Customer` e `CustomerData`. Qual è la differenza tra le due? Ecco perché è importante dare nomi sensati che ben distinguano le cose. 34 | 35 | ## Usate nomi pronunciabili 36 | 37 | Evitate di dare nomi che siano difficili da pronunciare e di conseguenza da scrivere. 38 | 39 | ## Usate nomi ricercabili 40 | 41 | I nomi devono essere anche facilmente ricercabili. Se usiamo nomi mono-lettera, quest'ultimi diventano impossibili da ricercare, poiché il risultato della ricerca darà migliaia di occorrenze. Evitate anche di usare magic numbers, meglio racchiuderli all'interno di costanti. 42 | 43 | Valore difficile da ricercare: 44 | 45 | ```text 46 | if ($status === 1) { 47 | // ... 48 | } 49 | ``` 50 | 51 | Valore facile da ricercare: 52 | 53 | ```text 54 | if ($status === Task::STATUS_CLOSED) { 55 | // ... 56 | } 57 | ``` 58 | 59 | ## Evitate le codifiche 60 | 61 | I nomi codificati sono difficili da ricordare e capire, soprattutto per chi è stato neo-assunto. 62 | 63 | ## Suffissi 64 | 65 | E' inutile aggiungere ai nomi di variabili, funzioni e interfacce dei suffissi. I moderni IDE, attraverso il Syntax Highlighting o tramite l'utilizzo di apposite icone, ci forniscono già delle informazioni che rendono inutile l'utilizzo di suffissi. 66 | 67 | Nome di interfaccia da evitare: 68 | 69 | ```text 70 | interface ShapeFactoryInterface 71 | ``` 72 | 73 | Nome di interfaccia corretto: 74 | 75 | ```text 76 | interface ShapeFactory 77 | ``` 78 | 79 | ## Nomi di classi 80 | 81 | Il nome di una classe non dovrebbe essere un verbo. Inoltre evitate parole come `Manager`, `Processor`, `Data` o `Info` che spesso vengono utilizzate solo per distinguere i nomi e non aggiungono altre informazioni su ciò che fa la classe. 82 | 83 | ## Nomi di metodi 84 | 85 | I nomi dei metodi dovrebbero contenere un verbo o una frase verbale, come `sendEmail`, `save`, `deleteCustomer`. Anche per i costruttori spesso è utile adottare un nome \([Named Constructor](http://blog.conorsmith.ie/named-constructors-in-php)\) per rendere più chiaro quest'ultimo. 86 | 87 | Costruttore poco chiaro: 88 | 89 | ```text 90 | $fulcrumPoint = new Complex(23.0); 91 | ``` 92 | 93 | Costruttore chiaro: 94 | 95 | ```text 96 | $fulcrumPoint = Complex::fromRealNumber(23.0); 97 | ``` 98 | 99 | ## Una parola, un concetto 100 | 101 | Scegliete una parola per un determinato concetto astratto e continuatela ad utilizzarla per tutto il codice. Per esempio, non ha senso utilizzare `fetch`, `retrieve` e `get` per metodi equivalenti di classi differenti. Se ad esempio abbiamo utilizzato `add` per la somma di due valori, non usiamo lo stesso nome per inserire un elemento all'interno di una lista. Piuttosto utilizzate qualche altro nome tipo `insert` o `append`. 102 | 103 | ## Usate nomi appartenenti al dominio 104 | 105 | Cercate di utilizzare nomi appartenenti al dominio dell'applicazione. 106 | 107 | ## Aggiungere un contesto al nome 108 | 109 | Aggiungendo un contesto al nome, quest'ultimo acquisisce maggiore chiarezza. Se prendo il nome `state`, vi risulta chiaro a cosa io mi stia riferendo? Se lo rinominassi in `addrState`, quest'ultimo diventerebbe subito più chiaro. Spesso anche creare una classe aiuta a dare contesto ai nomi \(Es. la classe `Address` per rappresentare i campi di un indirizzo\). 110 | 111 | ## Non aggiungete contesti superflui 112 | 113 | Non bisogna abusare dei contesti nei nomi se non ce n'è di bisogno. Il nome `CustomerAddress` è un buon nome per rappresentare l'istanza di una classe, ma diventa un cattivo nome nel momento in cui lo utilizzo come nome per una classe, in quanto è superfluo aggiungere il contesto `Customer` per rappresentare dei semplici campi di un indirizzo. 114 | 115 | -------------------------------------------------------------------------------- /oggetti-e-strutture.md: -------------------------------------------------------------------------------- 1 | # Oggetti e strutture 2 | 3 | Il motivo principale per cui manteniamo le variabili private è perché non vogliamo che qualcun altro conosca i dettagli dei nostri oggetti, ma utilizzi solamente i metodi per manipolare tali dati. Allora perché creiamo variabili private se poi aggiungiamo metodi _getter_ e _setter_ rendendo di fatto le variabili private come se fossere pubbliche? 4 | 5 | ### Astrazione dei dati 6 | 7 | Se creiamo oggetti con delle variabili private, dovremmo mantenere queste variabili private e fornire semplicemente dei metodi astratti che consentono di manipolare tali dati. In questo modo nascondiamo l'implementazione e forniamo solo i metodi che si occupano di manipolare l'essenza dei nostri dati. 8 | 9 | Veicolo concreto: 10 | 11 | ```text 12 | interface Vehicle { 13 | public function getFuelTankCapacityInGallons(): double; 14 | public function getGallonsOfGasoline(): double; 15 | } 16 | ``` 17 | 18 | Veicolo astratto: 19 | 20 | ```text 21 | interface Vehicle { 22 | public function getPercentFuelRemaining(): double; 23 | } 24 | ``` 25 | 26 | In questi due esempi, il secondo è preferibile. Non vogliamo esporre i dettagli dei nostri dati. Piuttosto vogliamo esprimere i nostri dati in termini astratti. 27 | 28 | ### Asimmetria dei dati/oggetti 29 | 30 | L'esempio sopra mostra la differenza tra **oggetti** e **strutture**. Gli **oggetti** nascondono i loro dati dietro astrazioni ed espongono le funzioni che operano su tali dati. Le **strutture** espongono i loro dati e non hanno alcuna funzione significativa. 31 | 32 | Esempio di utilizzo delle strutture: 33 | 34 | ```text 35 | class Square { 36 | public $side; 37 | } 38 | 39 | class Rectangle { 40 | public $height; 41 | public $width; 42 | } 43 | 44 | class Circle { 45 | public $center; 46 | public $radius; 47 | } 48 | 49 | class Geometry { 50 | private const PI = 3.141592653589793; 51 | 52 | public function area(object $shape): double { 53 | switch(true) { 54 | case $shape instanceof Square: 55 | return pow($shape->side, 2); 56 | case $shape instanceof Rectangle: 57 | return $shape->height * $shape->width; 58 | case $shape instanceof Circle: 59 | return pow($shape->radius, 2) * self::PI; 60 | default: 61 | throw new NoSuchShapeException(); 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | Esempio di utilizzo degli oggetti: 68 | 69 | ```text 70 | interface Shape { 71 | public function area(): double; 72 | } 73 | 74 | class Square implements Shape { 75 | private $side; 76 | 77 | public function __construct(double $side) { 78 | $this->side = $side; 79 | } 80 | 81 | public function area(): double { 82 | return pow($this->side, 2); 83 | } 84 | } 85 | 86 | class Rectangle implements Shape { 87 | private $height; 88 | private $width; 89 | 90 | public function __construct(double $height, double $width) { 91 | $this->height = $height; 92 | $this->width = $width; 93 | } 94 | 95 | public function area(): double { 96 | return $this->height * $this->width; 97 | } 98 | } 99 | 100 | class Circle implements Shape { 101 | private const PI = 3.141592653589793; 102 | private $center; 103 | private $radius; 104 | 105 | public function __construct(double $radius) { 106 | $this->radius = $radius; 107 | } 108 | 109 | public function area(): double { 110 | return pow($this->radius, 2) * self::PI; 111 | } 112 | } 113 | ``` 114 | 115 | ### In sintesi 116 | 117 | Da questi due esempi è possibile notare la differenza sostanziale tra oggetti e strutture. Se venisse aggiunta una nuova funzione `perimeter()` alla classe `Geometry`, le classi delle forme non subirebbero nessuna modifica e l'aggiunta della nuova funzione sarebbe piuttosto semplice, ma se venisse aggiunta una nuova forma `Triangle`, dovrei andare a modificare le funzioni esistenti all'interno di `Geometry`. Al contrario, nell'esempio degli oggetti, se venisse aggiunta una nuova forma `Triangle` non devo fare alcuna modifica alle funzioni esistenti, ma se venisse aggiunta una nuova funzione `perimeter()` dovrei andare a modificare tutte le forme! Possiamo quindi dedurre che oggetti e strutture sono l'una l'opposto dell'altra. Le **strutture** facilitano l'aggiunta di nuove funzioni senza modificare l'assetto esistente, ma qualora venisse aggiunta una nuova struttura, dovrò modificare tutte le funzioni esistenti. Gli **oggetti**, al contrario, facilitano l'aggiunta di nuove classi senza modificare le funzioni esistenti, ma complicano l'aggiunta di nuove funzioni, perchè tutte le classi devono cambiare. 118 | 119 | ### La legge di Demetra 120 | 121 | La _legge di Demetra_ dice che un modulo non dovrebbe conoscere i dettagli degli oggetti che manipola. Significa che un oggetto non dovrebbe mostrare la propria struttura interna tramite i metodi di accesso, perché ciò vorrebbe dire esporre, e non nascondere, la propria struttura interna. Nello specifico la legge di Demetra dice che un metodo _f_ di una classe _C_ dovrebbe richiamare solo i metodi di: 122 | 123 | * C 124 | * un oggetto creato da f 125 | * un oggetto passato come argomento a f 126 | * un oggetto contenuto in una variabile di istanza di C 127 | 128 | Un esempio di violazione di questa legge è dato dal seguente codice: 129 | 130 | ```text 131 | $outputDir = $ctxt->getOptions()->getScratchDir()->getAbsolutePath(); 132 | ``` 133 | 134 | ### Relitti ferroviari 135 | 136 | Questo genere di codice viene chiamato "relitto ferroviario" perché ha l'aspetto di un ammasso di coppie di vagoni e generalmente va evitato. Ritornando a noi, il codice sopra riportato, viola la legge di Demetra? Ciò dipende dal fatto che _ctxt_, _options_, e _scratchDir_ siano oggetti o strutture. Se sono oggetti, allora la loro struttura interna dovrebbe essere nascosta, pertanto viola la legge di Demetra. Se sono strutture, e quindi non hanno funzioni che fanno qualcosa di significativo, è normale che espongono i propri dati interni, pertanto **non** viola la legge di Demetra. L'utilizzo dei metodi di accesso confonde la situazione. 137 | 138 | ### Ibridi 139 | 140 | Questo talvolta porta ad avere degli ibridi: per metà oggetti e per metà strutture. Hanno funzioni che fanno qualcosa di significativo, ma hanno anche _getter_ e _setter_ che espongono le variabili private. 141 | 142 | ### Nascondere la struttura 143 | 144 | E se _ctxt_, _options_ e _scratchDir_ fossero oggetti? Come faccio a ricavarmi il percorso assoluto di scratch? Se _ctxt_ fosse un oggetto, dovremmo chiedergli di **fare qualcosa** e non chiedergli i suoi dettagli interni. Continuando con il codice vediamo a cosa serve _outputDir_: 145 | 146 | ```text 147 | $outFilename = $outputDir . '/' . str_replace('.', '/', $className) . '.class'; 148 | $file = fopen($outFilename, 'w'); 149 | ``` 150 | 151 | Vediamo quindi come _outputDir_ serve per creare un file, ma allora perché non farlo creare direttamente a _ctxt_? 152 | 153 | ```text 154 | $file = $ctxt->createScratchFile($classFilename); 155 | ``` 156 | 157 | Ciò consente a _ctxt_ di nascondere la propria struttura interna ed evita la violazione della legge di Demetra. 158 | 159 | ### Data Transfer Object \(DTO\) 160 | 161 | Una struttura è una classe con variabili pubbliche \(o private, ma manipolate tramite _getter_ e _setter_\) e senza funzioni, un cosiddetto **Data Transfer Object** o **DTO**. I DTO sono utili soprattutto per comunicare con i database. Servono per trasformare il dato "grezzo" di una tabella in oggetti dell'applicazione. 162 | 163 | ```text 164 | class Address { 165 | private $state; 166 | private $province; 167 | private $city; 168 | private $address; 169 | private $zip; 170 | 171 | public function __construct( 172 | string $state, 173 | string $province, 174 | string $city, 175 | string $address, 176 | string $zip 177 | ) { 178 | $this->state = $state; 179 | $this->province = $province; 180 | $this->city = $city; 181 | $this->address = $address; 182 | $this->zip = $zip; 183 | } 184 | 185 | public function getState(): string { 186 | return $this->state; 187 | } 188 | 189 | public function getProvince(): string { 190 | return $this->province; 191 | } 192 | 193 | public function getCity(): string { 194 | return $this->city; 195 | } 196 | 197 | public function getAddress(): string { 198 | return $this->address; 199 | } 200 | 201 | public function getZip(): string { 202 | return $this->zip; 203 | } 204 | } 205 | ``` 206 | 207 | ### Active Record 208 | 209 | Gli **Active Record** sono delle forme particolari di DTO. Sono simili ai DTO, ma sono dotati di metodi di **navigazione** come _save_ e _find_. Sfortunatamente spesso si tende a trattare queste strutture come se fossero oggetti, inserendovi anche metodi di manipolazione. Evitatelo! Piuttosto create degli oggetti appositi che contengono le regole operative e che nascondono i propri dati interni. 210 | 211 | -------------------------------------------------------------------------------- /simple-design.md: -------------------------------------------------------------------------------- 1 | # Simple Design 2 | 3 | Esistono quattro semplici regole che ci consentono di scrivere del codice pulito. Queste regole, descritte per la prima volta da Kent Beck in **Simple Design**, ci dicono che un progetto è semplice se: 4 | 5 | * Passa tutti i test 6 | * Non contiene duplicazioni 7 | * Esprime lo scopo del programmatore 8 | * Minimizza il numero di classi e metodi 9 | 10 | Le regole sono elencate in ordine di priorità. 11 | 12 | ### Regola 1 - Passa tutti i test 13 | 14 | Un sistema che ha il 90% del codice coperto da test è un sistema collaudabile. Il fatto di rendere collaudabile un sistema favorisce una progettazione in cui le nostre classi sono piccole e svolgono un unico compito \(SRP\). Uno stretto accoppiamento complica la creazione di test. Pertanto più test scriviamo e più usiamo principi come il DIP e strumenti come la Dependency Injection, le interfacce e l'astrazione per ridurre al minimo l'accoppiamento. 15 | 16 | ### Regola 2 - Niente duplicazione 17 | 18 | Una volta che abbiamo i nostri test, possiamo rifattorizzare il codice senza alcun timore eliminando le duplicazioni presenti. La duplicazione è il nemico numero uno in un progetto, perché introduce rischi e complica inutilmente il codice. Per eliminarla dobbiamo estrarre ad un livello minuto ciascun metodo, cosicché possiamo riconoscere un eventuale violazione del principio SRP, il quale ci porterà ad estrarre un ulteriore classe. Il pattern [**Template Method**](https://it.wikipedia.org/wiki/Template_method) è molto utilizzato per eliminare la duplicazione. 19 | 20 | Prendiamo per esempio questa classe che si occupa di calcolare il numero di giorni di vacanza in base alla nazionalità del dipendente: 21 | 22 | ```text 23 | class VacationPolicy { 24 | public function accrueUSDivisionVacation(): void { 25 | // codice per calcolare le vacanze sulla base del numero di ore lavorate 26 | 27 | // codice per garantire il minimo di legge USA in giorni di vacanza 28 | 29 | // codice per considerare il periodo di vacanza nella paga 30 | } 31 | 32 | public function accrueEUDivisionVacation(): void { 33 | // codice per calcolare le vacanze sulla base del numero di ore lavorate 34 | 35 | // codice per garantire il minimo di legge EU in giorni di vacanza 36 | 37 | // codice per considerare il periodo di vacanza nella paga 38 | } 39 | } 40 | ``` 41 | 42 | Il codice di queste due funzioni è quasi uguale, l'unica differenza sta nel codice per calcolare il minimo di legge. Possiamo eliminare questa duplicazione utilizzando il pattern **Template Method**: 43 | 44 | ```text 45 | abstract class VacationPolicy { 46 | public function accrueVacation(): void { 47 | $this->calculateBaseVacationHours(); 48 | $this->alterForLegalMinimums(); 49 | $this->applyToPayroll(); 50 | } 51 | 52 | private function calculateBaseVacationHours(): void { ... } 53 | 54 | abstract protected function alterForLegalMinimums(): void; 55 | 56 | private function applyToPayroll(): void { ... } 57 | } 58 | 59 | class USVacationPolicy { 60 | protected function alterForLegalMinimums(): void { ... } 61 | } 62 | 63 | class EUVacationPolicy { 64 | protected function alterForLegalMinimums(): void { ... } 65 | } 66 | ``` 67 | 68 | ### Regola 3 - Espressività 69 | 70 | La maggior parte del costo di un progetto software è la sua manutenzione a lungo termine. Aumentando la complessità del sistema, uno sviluppatore avrà bisogno di sempre più tempo per comprenderlo. Pertanto, il codice deve esprimere chiaramente lo scopo del suo autore. 71 | 72 | Possiamo curare l'espressività nei seguenti modi: 73 | 74 | * Scegliendo buoni nomi 75 | * Mantenendo piccole le funzioni e le classi 76 | * Usando sistemi di denominazione standard. Per esempio se usiamo i pattern **Command** o **Visitor**, possiamo aggiungere questi nomi al nome della classe 77 | * Scrivere unit test. Un obiettivo importante dei test è quello di fungere da documentazione basata sull'esempio 78 | 79 | ### Regola 4 - Minimizzare classi e metodi 80 | 81 | Nel tentativo di compattare le nostre classi e metodi, potremmo correre il rischio di creare classi e metodi troppo piccoli. Pertanto bisogna cercare di essere pragmatici e contenere il numero di classi e metodi \(ad esempio creare un'interfaccia per ogni classe non ha senso\). 82 | 83 | -------------------------------------------------------------------------------- /sistemi.md: -------------------------------------------------------------------------------- 1 | # Sistemi 2 | 3 | In questo capitolo parleremo della pulizia del codice e della separazione degli ambiti a livello di sistema. 4 | 5 | ### Separate la realizzazione dall'uso di un sistema 6 | 7 | I sistemi software dovrebbero separare il processo di avvio, ovvero quando vengono istanziati gli oggetti dell'applicazione e vengono stabilite le dipendenze, dalla logica runtime che si esprime dopo l'avvio. 8 | 9 | ```text 10 | class MyClass { 11 | public function getService(): Service { 12 | if ($this->service === null) { 13 | $this->service = new Service(); 14 | } 15 | return $this->service; 16 | } 17 | } 18 | ``` 19 | 20 | Questa si chiama "lazy initialization", ovvero la costruzione dell'oggetto avviene solo nel momento in cui abbiamo effettivamente bisogno di quell'oggetto. Tuttavia così abbiamo creato una dipendenza diretta con la classe `Service` e da tutto ciò che richiede il suo costruttore \(che per semplicità ho lasciato vuoto\). Questo non solo viola il principio SRP, in quanto se cambia il costruttore della classe `Service` sarà costretta a cambiare anche questa classe, ma stiamo anche precludendo il fatto che la classe `Service` vada bene in ogni contesto. Per questo occorre separare il processo di avvio dalla logica runtime. 21 | 22 | ### Separazione di main 23 | 24 | Una soluzione per separare la costruzione degli oggetti dal loro utilizzo consiste nel trasferire tutti gli aspetti della costruzione all'interno di `main`, che non fa altro che costruire gli oggetti e passarli all'applicazione. Quest'ultima sarà quindi all'oscuro di come gli oggetti vengono costruiti, semplicemente si aspetta che tutto sia stato già costruito. 25 | 26 | ### Factory 27 | 28 | Talvolta è necessario che l'applicazione sia responsabile del momento in cui l'oggetto viene creato. In questi casi possiamo usare il pattern [Abstract Factory](https://en.wikipedia.org/wiki/Abstract_factory_pattern), così da permettere all'applicazione di controllare la creazione dell'oggetto, pur mantenendo i dettagli della costruzione separati rispetto al codice dell'applicazione. 29 | 30 | ### Dependency Injection 31 | 32 | Un potente meccanismo per separare la costruzione degli oggetti dal loro utilizzo è chiamato **Dependency Injection \(DI\)**, che è l'applicazione dell'inversione del controllo \([Inversion of Control - IoC](https://it.wikipedia.org/wiki/Inversione_del_controllo)\) alla gestione delle dipendenze. Nel contesto della gestione delle dipendenze, un oggetto non dovrebbe assumersi la responsabilità di istanziare le proprie dipendenze. Questa responsabilità deve essere trasferita altrove. Nella Dependency Injection la classe fornisce dei metodi setter o degli argomenti del costruttore \(o entrambi\), che vengono utilizzati per iniettare le dipendenze. Il container DI istanzia gli oggetti richiesti e li passa al costruttore o ai metodi setter della classe. Quali oggetti dipendenti vengono usati in realtà viene specificato all'interno di un file di configurazione \(xml / yml\). 33 | 34 | ### Confusione di ambiti 35 | 36 | All'interno di un sistema il codice deve essere modulare, ci deve essere una netta separazione degli ambiti. Per esempio in un sistema in cui è presente la persistenza degli oggetti in memoria, la logica che si occupa della persistenza deve essere incapsulata e separata rispetto alla logica di dominio. Questa separazione degli ambiti, oltre a rendere più semplice la scrittura dei test, favorisce l'evoluzione incrementale del nostro codice. 37 | 38 | ### Sottoporre a test l'architettura del sistema 39 | 40 | Il vantaggio di separare gli ambiti all'interno dell'architettura del sistema porta ad avere un codice disaccoppiato e facilmente testabile. Così facendo possiamo far evolvere la nostra applicazione, adottando le nuove tecnologie disponibili, senza adottare soluzioni in stile [**BDUF**](https://en.wikipedia.org/wiki/Big_Design_Up_Front) **\(Big Design Up Front\)**. BDUF è la pratica che consiste nel progettare tutto in anticipo, prima ancora di iniziare ogni implementazione. Questa pratica può essere pericolosa, poiché non favorisce gli adattamenti a causa della resistenza psicologica a scartare ogni decisione che va fuori dalla pianificazione iniziale. Se invece abbiamo una struttura in cui l'accoppiamento è minimo, possiamo facilmente far evolvere la nostra struttura man mano che il progetto cresce di dimensioni. 41 | 42 | ### Usate gli standard con cura, solo se dimostrano il proprio valore 43 | 44 | Molti team utilizzano gli standard solo perché tali, seppur mal si sposano con le reali esigenze di progetto. Gli standard vanno utilizzati solo se dimostrano realmente il proprio valore e non rappresentano invece un ostacolo. 45 | 46 | -------------------------------------------------------------------------------- /unit-test.md: -------------------------------------------------------------------------------- 1 | # Unit test 2 | 3 | Affinché siamo sicuri che il nostro codice funzioni, quest'ultimo deve essere sottoposto a test. Tuttavia i test non devono rappresentare una massa di codice usa-e-getta che si occupa solamente di testare il nostro codice. Essi devono essere curati, così come deve essere curato il nostro codice di produzione. 4 | 5 | ### Le tre leggi dello sviluppo TDD 6 | 7 | Lo sviluppo TDD \(Test-Driven Development\) ci chiede di scrivere per primi gli unit test, prima ancora di scrivere il codice effettivo del software. Il principio dello sviluppo TDD si può riassumere in tre leggi: 8 | 9 | * Non bisogna scrivere il codice di produzione finché non si è scritto prima uno unit test 10 | * Non bisogna procedere con gli unit test più di quanto sia sufficiente per ottenere un fallimento 11 | * Non bisogna procedere con il codice di produzione più di quanto sia sufficiente per passare gli attuali unit test 12 | 13 | Da queste leggi si può capire come unit test e codice di produzione vanno scritti **insieme**. Se lavoriamo in questo modo, i test copriranno tutto il codice di produzione. 14 | 15 | ### Curate la pulizia dei test 16 | 17 | Il codice dei test è importante quanto il codice di produzione. Avere dei test scritti bene e che rispettono gli stessi standard qualitativi del codice di produzione, ci aiuta ad apportare delle modifiche ai test quando ce n'è di bisogno. Il motivo principale è che i test devono cambiare insieme al codice di produzione. Più scadenti sono i test, più sarà difficile modificarli. 18 | 19 | ### I test garantiscono le "...bilità" 20 | 21 | Gli unit test garantiscono la flessibilità, manutenibilità e riutilizzabilità del nostro codice. Se abbiamo dei test scritti bene, non avremo mai paura di modificare il nostro codice! Maggiore è la copertura dei test, minore è la paura. Se i test sono scadenti, ciò ci indurrà a non modificare e migliorare il nostro codice. Peggiori sono i test e peggiore diventerà il nostro codice. 22 | 23 | ### Un doppio standard 24 | 25 | Il codice di test deve sottostare ad un insieme di standard progettuali differente rispetto al codice di produzione. Deve essere chiaro, semplice ed espressivo, ma non deve essere altrettanto efficiente come il codice di produzione. Questo perché il codice di test viene eseguito in un ambiente per l'appunto di test, dove le risorse di memoria RAM e CPU sono differenti rispetto all'ambiente di produzione. 26 | 27 | ### Una sola richiesta per test 28 | 29 | Vi è una scuola di pensiero che dice che ogni funzione di test deve avere una e una sola istruzione `assert`. Questo aiuta a scrivere dei test chiari e concisi. Tuttavia a volte questa regola diventa difficile da applicare, in quanto richiederebbe la strutturazione di classi troppo complesse. Pertanto consiglio di ridurre al minimo il numero di `assert` presenti all'interno di una funzione di test, ma senza vincolarsi troppo seguendo questa regola. 30 | 31 | ### Un unico concetto per ogni test 32 | 33 | Per ogni funzione di test che scriviamo, dobbiamo sottoporre a test un unico concetto. Questo ci evita di realizzare lunghe funzioni che applicano tanti test disparati. 34 | 35 | ### F.I.R.S.T 36 | 37 | Un test è "pulito" se obbedisce a queste cinque regole che formano l'acronimo F.I.R.S.T 38 | 39 | * _Fast_: I test dovrebbero essere veloci da eseguire. Se i test sono lenti, eviteremo di eseguirli con la giusta frequenza. 40 | * _Independent_: I test non dovrebbero dipendere l'uno dall'altro. Dovremmo essere in grado di eseguire i test in qualsiasi ordine. 41 | * _Repeatable_: I test dovrebbero essere ripetibili in qualsiasi ambiente. 42 | * _Self-Validating_: I test dovrebbero essere autoconclusivi e avere un output booleano. Il test o ha successo o fallisce. 43 | * _Timely_: I test devono essere aggiornati. Gli unit test dovrebbero essere scritti appena prima del codice di produzione cui fanno riferimento e devono cambiare insieme al codice di produzione. 44 | 45 | --------------------------------------------------------------------------------