Una funzione pura è quella che:
- Fornirà sempre lo stesso risultato dati gli stessi argomenti
- Non ha effetti collaterali osservabili (ad esempio cambiamenti di stato)
Supponiamo di scrivere del codice per gestire l'accesso dell'utente, in cui vogliamo verificare che il nome utente e la password forniti siano corretti e impedire all'utente di accedere se ci sono troppi tentativi falliti. In uno stile imperativo il nostro codice potrebbe apparire così:
bool UserLogin(string username, string password)
{
var user = _database.FindUser(username);
if (user == null)
{
return false;
}
if (user.FailedAttempts > 3)
{
return false;
}
// Password hashing omitted for brevity
if (user.Password != password)
{
_database.RecordFailedLoginAttempt(username);
}
return true;
}
È abbastanza chiaro che questa non è una funzione pura:
- Questa funzione non fornirà sempre lo stesso risultato per una data
usernamee una passwordcombinazione poiché il risultato dipende anche dal record dell'utente memorizzato nel database.
- La funzione può cambiare lo stato del database, ovvero ha effetti collaterali.
Inoltre, per testare l'unità questa funzione dobbiamo prendere in giro due chiamate al database FindUsere RecordFailedLoginAttempt.
Se dovessimo riformattare questo codice in uno stile più funzionale potremmo finire con qualcosa di simile a questo:
bool UserLogin(string username, string password)
{
var user = _database.FindUser(username);
var result = UserLoginPure(user, password);
if (result == Result.FailedAttempt)
{
_database.RecordFailedLoginAttempt(username);
}
return result == Result.Success;
}
Result UserLoginPure(User user, string pasword)
{
if (user == null)
{
return Result.UserNotFound;
}
if (user.FailedAttempts > 3)
{
return Result.LoginAttemptsExceeded;
}
if (user.Password != password)
{
return Result.FailedAttempt;
}
return Result.Success;
}
Si noti che sebbene la UserLoginfunzione non sia ancora pura, la UserLoginPurefunzione ora è una funzione pura e di conseguenza la logica di autenticazione dell'utente principale può essere testata dall'unità senza dover deridere eventuali dipendenze esterne. Questo perché l'interazione con il database è gestita più in alto nello stack di chiamate.