Як функціональний стиль допомагає знущатися із залежностей?


10

З інтерв'ю з Кентом Беком у недавньому номері журналу Java:

Бінсток: Давайте обговоримо мікросервіси. Мені здається, що перший тест на мікросервісах ускладнився б в тому сенсі, що деяким службам, щоб функціонувати, знадобиться наявність цілого ряду інших сервісів. Ви згодні?

Бек: Схоже, той самий набір професійних пропозицій щодо того, щоб мати великий клас або багато маленьких класів.

Бінсток: Так, хіба що, мабуть, тут вам доведеться використовувати дуже багато глузувань, щоб мати можливість створити систему, за допомогою якої ви можете протестувати певну послугу.

Бек: Я не згоден. Якщо це в імперативному стилі, вам доведеться використовувати макети. У функціональному стилі, коли зовнішні залежності збираються разом високо в ланцюзі викликів, я не думаю, що це потрібно. Я думаю, що ви можете отримати багато покриття з одиничних тестів.

Що він означає? Як функціональний стиль може звільнити вас від глузування із зовнішніх залежностей?


1

1
Якщо вони конкретно обговорюють Java, я підозрюю, що більша частина дискусії є суперечкою. Java насправді не має такої підтримки, яку вона потребує, щоб описати тип функціонального програмування. О, звичайно, ви можете використовувати утилітні класи або, можливо, Java 8 Lambdas, щоб імітувати це, але ... blecch.
Роберт Харві

Відповіді:


8

Чистий функція є той , який:

  1. Буду завжди дають той же результат , враховуючи ті ж аргументи
  2. Не має помітних побічних ефектів (наприклад, зміни стану)

Припустимо, ми пишемо якийсь код для обробки входу користувачів, де ми хочемо перевірити правильність наданих імені користувача та пароля та запобігти входу користувача, якщо занадто багато невдалих спроб. В імперативному стилі наш код може виглядати так:

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;
}

Цілком зрозуміло, що це не чиста функція:

  1. Ця функція не завжди дає той же результат для заданого usernameі passwordкомбінації , як результат також залежить від записи користувача , що зберігається в базі даних.
  2. Функція може змінювати стан бази даних, тобто вона має побічні ефекти.

Також зауважте, що для тестування цієї функції нам потрібно знущатися над двома дзвінками до бази даних FindUserта RecordFailedLoginAttempt.

Якби ми переробили цей код у більш функціональний стиль, ми могли б закінчитися чимось таким:

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;
}

Зауважте, що хоча UserLoginфункція все ще не є чистою, UserLoginPureтепер функція є чистою функцією, і в результаті основна логіка автентифікації користувача може бути перевірена одиницею без необхідності висміювати будь-які зовнішні залежності. Це пояснюється тим, що взаємодія з базою даних обробляється вище до стека викликів.


Чи є ваше тлумачення: імперативний стиль = державні мікропослуги та функціональний стиль = мікросервіси без громадянства ?
k3b

@ k3b Сортування, крім мікроелементів про мікропослуги. Дуже просто імперативний стиль передбачає маніпулювання станом, тоді як функціональний стиль використовує чисті функції без маніпулювання станом.
Джастін

1
@Justin: Я б сказав, що функціональний стиль чітко відокремлює чисті функції від коду з побічними ефектами, як ви робили у своєму прикладі. Іншими словами, функціональний код все ще може мати побічні ефекти.
Джорджіо

Функціональний підхід повинен повернути пару з результатом і користувачем, тому що при невдалій спробі Result.FailedAttempt є результатом нового користувача з тими ж даними, що й оригінал, за винятком того, що він має ще одну невдалу спробу і чисту функцію робить викликати побічні ефекти для користувача, які задаються як параметр.
підйом Темнота

виправлення останньої частини мого попереднього коментаря: "і чиста функція НЕ викликає побічних ефектів для користувача, які задаються як параметр."
темності
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.