機能スタイルは依存関係のモックにどのように役立ちますか?


10

最近のJava Magazine号のKent Beckへのインタビューから:

Binstock:マイクロサービスについて説明しましょう。一部のサービスが機能するためには、他のサービスの束全体の存在が必要になるという意味で、マイクロサービスのテストファーストは複雑になるように思えます。同意しますか?

ベック:1つの大きなクラスまたはたくさんの小さなクラスを持つことについては、同じトレードオフのセットのようです。

Binstock:そうです、私が推測する場合を除いて、特定のサービスをテストできるシステムをセットアップできるようにするためには、ここでは非常に多くのモックを使用する必要があります。

ベック:そう思わない。命令型のスタイルの場合は、モックをたくさん使用する必要があります。外部依存関係がコールチェーンの上位に集められている関数型のスタイルでは、それは必要ないと思います。単体テストから多くの報道を得ることができると思います。

彼はどういう意味ですか?機能的なスタイルで、外部の依存関係をあざけることからどのように解放できますか



1
彼らが具体的にJavaについて議論しているのであれば、その議論の多くは議論の余地があると思います。Javaは、説明されている種類の関数型プログラミングに役立てるために必要な種類のサポートを実際には持っていません。確かに、ユーティリティクラスまたはJava 8 Lambdasを使用してシミュレートすることはできますが、残念です。
ロバートハーヴェイ

回答:


8

純粋な関数が 1ということです。

  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. 関数はデータベースの状態を変更できます。つまり、副作用があります。

また、この機能ユニットテストするために、我々は2つのデータベース・コールをモックする必要があることに注意してください、FindUserRecordFailedLoginAttempt

このコードをより機能的なスタイルにリファクタリングすると、次のような結果になる可能性があります。

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 2017年

@ k3bちょっと、マイクロサービスについてのビットを除いて。非常に単純な命令型スタイルは状態の操作を含み、関数型スタイルは状態の操作なしで純粋な関数を使用します。
ジャスティン

1
@ジャスティン:関数スタイルでは、例で行ったように、副作用のあるコードから純粋な関数を明確に分離すると思います。言い換えれば、関数型コードには依然として副作用があります。
ジョルジオ

機能的なアプローチは、結果とユーザーのペアを返す必要があります。失敗した試行では、Result.FailedAttemptは、元のユーザーと同じデータを持つ新しいユーザーの結果です。パラメータとして指定されたユーザーに副作用を誘発します。
risingDarkness 2017年

私の以前のコメントの最後の部分の修正:「そして純粋な関数パラメーターとして与えられるユーザーに副作用を引き起こしません。」
risingDarkness 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.