Czysta funkcja to taka, która:
- Będzie zawsze dawać ten sam wynik, biorąc pod uwagę te same argumenty
- Nie ma żadnych obserwowalnych skutków ubocznych (np. Zmian stanu)
Załóżmy, że piszemy kod do obsługi logowania użytkownika, w którym chcemy sprawdzić, czy podana nazwa użytkownika i hasło są prawidłowe, i uniemożliwić zalogowanie się użytkownika, jeśli będzie zbyt wiele nieudanych prób. W trybie rozkazującym nasz kod może wyglądać następująco:
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;
}
Jest całkiem jasne, że nie jest to czysta funkcja:
- Ta funkcja nie zawsze daje taki sam wynik dla danej kombinacji
usernamei passwordkombinacji, ponieważ wynik zależy również od rekordu użytkownika przechowywanego w bazie danych.
- Funkcja może zmienić stan bazy danych, tzn. Ma skutki uboczne.
Zauważ też, że aby przetestować tę funkcję, musimy wykpić dwa wywołania bazy danych FindUseri RecordFailedLoginAttempt.
Gdybyśmy zmienili kod na bardziej funkcjonalny, moglibyśmy otrzymać coś takiego:
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;
}
Zauważ, że chociaż UserLoginfunkcja ta nadal nie jest czysta, UserLoginPurejest ona teraz funkcją czystą, w wyniku czego logika uwierzytelniania podstawowego użytkownika może być testowana jednostkowo bez konieczności kpienia z zewnętrznych zależności. Jest tak, ponieważ interakcja z bazą danych jest obsługiwana wyżej w stosie wywołań.