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
username
e una password
combinazione 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 FindUser
e 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 UserLogin
funzione non sia ancora pura, la UserLoginPure
funzione 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.