In che modo lo stile funzionale aiuta con le dipendenze di derisione?

8

Dall'intervista con Kent Beck in un recente numero di Java Magazine:

Binstock: Let’s discuss microservices. It seems to me that test-first on microservices would become complicated in the sense that some services, in order to function, will need the presence of a whole bunch of other services. Do you agree?

Beck: It seems like the same set of trade-ofs about having one big class or lots of little classes.

Binstock: Right, except I guess, here you have to use an awful lot of mocks in order to be able to set up a system by which you can test a given service.

Beck: I disagree. If it is in an imperative style, you do have to use a lot of mocks. In a functional style where external dependencies are collected together high up in the call chain, then I don’t think that’s necessary. I think you can get a lot of coverage out of unit tests.

Che cosa intende? In che modo lo stile funzionale può liberarti dalle derisioni delle dipendenze esterne?

    
posta Dan 17.01.2017 - 20:29
fonte

1 risposta

8

Una funzione pura è una che:

  1. Will sempre darà lo stesso risultato con gli stessi argomenti
  2. Non ha effetti collaterali osservabili (ad esempio cambiamenti di stato)

Supponiamo di scrivere del codice per gestire l'accesso 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 come questo:

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:

  1. Questa funzione non darà sempre lo stesso risultato per una determinata combinazione username e password poiché il risultato dipende anche dal record utente memorizzato nel database.
  2. La funzione può cambiare lo stato del database, cioè ha effetti collaterali.

Nota inoltre che per testare questa funzione è necessario prendere in giro due chiamate al database, FindUser e RecordFailedLoginAttempt .

Se dovessimo ridefinire questo codice in uno stile più funzionale potremmo ritrovarci 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 funzione UserLogin non sia ancora pura, la funzione UserLoginPure è ora una funzione pura e, di conseguenza, la logica di autenticazione dell'utente principale può essere sottoposta a test dell'unità senza la necessità di prendere in giro eventuali dipendenze esterne. Questo perché l'interazione con il database viene gestita più in alto nello stack di chiamate.

    
risposta data 18.01.2017 - 00:43
fonte

Leggi altre domande sui tag