Nel precedente articolo ho iniziato a parlare di alcune tecniche di refactoring utilizzate più di frequente. Purtroppo il codice che possiamo trovarci di fronte non è sempre chiaro e ben organizzato, e quindi non è sempre facile capire la situazione nella quale ci si trova e quale tecnica di refactoring sarebbe necessario applicare. Per cercare di organizzare meglio il codice le operazioni di supporto più comuni sono quelle che normalmente vengono applicate alle variabili e permettono di semplificare le espressioni o rimuovere le variabili temporanee inutili. Spesso queste regole vengono utilizzate in relazione con le tecniche viste nell'articolo precedente al fine di facilitarne l'utilizzo.
In questo articolo analizzeremo alcune delle tecniche che normalmente si applicano alle variabili, partendo da quelle utilizzate più comunemente.
Inline Temp e Replace Temp with Query
Iniziamo parlando di due tecniche usate comunemente insieme. Inline Temp viene utilizzata quando si ha una variabile temporanea a cui il valore è assegnato un'unica volta. Se l'espressione assegnata è semplice e la variabile ha in qualche modo a che vedere con un'operazione di refactoring, allora possiamo sostituire l'occorrenza di questa variabile con l'espressione assegnata. Replace Temp with Query invece viene utilizzata quando usiamo una variabile temporanea per contenere il risultato di un'espressione o blocco di codice. Parliamo di queste regole in parallelo perché solitamente Inline Temp viene utilizzata dopo che Replace Temp with Query è stata applicata.
Vediamo un esempio in cui entrambe le regole sono applicate, da cui partire con l'analisi di queste tecniche.
class CartProduct
{
// ...
public function calculateTotalPrice()
{
$base_price = $this->quantity * $this->item_price;
if( $base_price > 10000 )
{
return $base_price * 0.90;
} else
{
return $base_price * 1;
}
}
}
Partendo da questo codice notiamo che il valore della variabile $base_price
viene assegnato una sola volta ed oltretutto è utilizzato per contenere il risultato del nostro metodo calculateTotalPrice
. in questo caso è importante notare che la variabile temporanea può creare alcuni problemi: in primis non ci permette di riutilizzare il codice utilizzato per calcolarla da un'altra parte, ma obbliga il copia-incolla dei sorgenti con il risultato che ogni modifica sul come calcolare il prezzo base vada fatta in ogni porzione di codice interessata; ed oltretutto si rischia che venga incoraggiata la scrittura di metodi molto lunghi dato che l'unico modo per accedere allo stesso valore originale è quello di ripetere la porzione di codice utilizzata per il calcolo.
Applichiamo quindi Replace Temp with Query:
class CartProduct
{
// ...
public function calculateTotalPrice()
{
$base_price = $this->calculateBasePrice();
if( $base_price > 10000 )
{
return $base_price * 0.90;
} else
{
return $base_price * 1;
}
}
public function calculateBasePrice()
{
return $this->quantity * $this->item_price;
}
}
Abbiamo estratto l'espressione assegnata alla variabile temporanea in un metodo (calculateBasePrice
) ed abbiamo semplicemente sostituito l'espressione con una chiamata al metodo originale. Successivamente rimuoviamo tutti i riferimenti alla variabile temporanea applicando Inline Temp:
class CartProduct
{
// ...
public function calculateTotalPrice()
{
if( $this->calculateBasePrice() > 10000 )
{
return $this->calculateBasePrice() * 0.90;
} else
{
return $this->calculateBasePrice() * 1;
}
}
public function calculateBasePrice()
{
return $this->quantity * $this->item_price;
}
}
Ora siamo ad un ottimo punto per applicare, se fosse necessario, altre tecniche di refactoring, come ad esempio Extract Method. Le variabili temporanee possono creare problemi se vogliamo estrarre due porzioni di codice che hanno a che vedere con una variabile temporanea comune in due metodi differenti, mentre dopo aver applicato questa tecnica di refactoring il problema non sussite più perché la variabile temporanea è stata rimossa.
Introduce Explaining Variable
Può capitare che ci si trovi nella situazione opposta rispetto a quella elencata sopra: difatti è possibile che nel codice siano utilizzate delle espressioni molto lunghe che rischiano di rendere molto complessa la lettura di alcune porzioni di codice. Spesso ci si trova di fronte a questa situazione quando si ha a che fare con espressioni condizionali che prendono in considerazione differenti situazioni per restituire valore vero o falso. In questo caso se l'espressione è troppo complessa è il caso di assegnare il risultato di alcune parti dell'espressione a delle variabili temporanee esplicative, che rendano più semplice interpretare l'espressione finale.
Facciamo un semplice esempio che si occupa di controllare alcune condizioni per decidere se applicare uno sconto ad un prodotto:
class CartProduct
{
// ...
public function calculateTotalPrice()
{
if( $this->calculateBasePrice() > 10000 && $this->quantity > 10 && $this->user->times_online > 10 )
{
return $this->calculateBasePrice() * 0.90;
} else
{
return $this->calculateBasePrice() * 1;
}
}
public function calculateBasePrice()
{
return $this->quantity * $this->item_price;
}
}
Possiamo suddividere l'espressione in diverse parti per avere un'idea più chiara delle valutazioni che vengono fatte per decidere se applicare lo sconto:
class CartProduct
{
// ...
public function calculateTotalPrice()
{
$priceIsEnought = $this->calculateBasePrice() > 10000;
$itemsFillABox = $this->quantity > 10;
$frequentUser = $this->user->times_online > 10;
if( $priceIsEnought && $itemsFillABox && $frequentUser )
{
return $this->calculateBasePrice() * 0.90;
}else
{
return $this->calculateBasePrice() * 1;
}
}
public function calculateBasePrice()
{
return $this->quantity * $this->item_price;
}
}
Volendo potremmo anche procedere con il refactoring estraendo dei metodi che calcolino le singole condizioni, in modo da facilitarci il lavoro nel caso in cui il calcolo delle singole condizioni possa variare in futuro.
Split Temporary Variable
Alcune volte non riusciamo ad applicare le regole precedenti perché la variabile temporanea su cui si va ad operare subisce più di un assegnamento nel codice. Nel caso in cui questa variabile non serva per collezionare dei dati (come un totale) e non venga utilizzata all'interno di un loop, possiamo creare una variabile temporanea per ogni assegnamento così da facilitarci l'applicazione delle altre regole.
Vediamo un esempio anche se questa regola è molto semplice da applicare:
class Rectangle
{
// ...
public function dumpData()
{
$temp = 2 *( $this->width + $this->height);
echo "Perimetro: ".$temp."<br />";
$temp = $this->width * $this->height;
echo "Area: ".$temp."<br />";
}
}
In questo caso il calcolo del perimetro ed il calcolo dell'area possono essere assegnati a due diverse variabili temporanee.
class Rectangle
{
// ...
public function dumpData()
{
$perimeter = 2 *( $this->width + $this->height);
echo "Perimetro: ".$perimeter."<br />";
$area = $this->width * $this->height;
echo "Area: ".$area."<br />";
}
}
Conclusione
Abbiamo visto alcune tecniche che vengono normalmente utilizzate per facilitare l'applicazione di altre tecniche di refactoring più complesse. Spesso alcune tecniche di refactoring sono molto più utili per chiarire al programmatore quale tecnica più complessa applicare che in modo fine a se stesso, e le regole sopra elencate ne sono un buon esempio.