├── 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 |
--------------------------------------------------------------------------------