기능적 스타일은 의존성 조롱에 어떻게 도움이됩니까?


10

최근 Java Magazine 호에서 Kent Beck과의 인터뷰에서 :

빈 스톡 : 마이크로 서비스에 대해 논의 해 보자. 마이크로 서비스에 대한 테스트 우선은 일부 서비스가 작동하기 위해서는 다른 서비스가 많이 필요하다는 점에서 복잡해질 것 같습니다. 동의하십니까?

Beck : 하나의 큰 클래스 나 많은 작은 클래스를 갖는 것과 같은 트레이드 오브 트레이드 인 것 같습니다.

빈 스탁스 : 맞습니다. 여기서는 주어진 서비스를 테스트 할 수있는 시스템을 설정하려면 엄청나게 많은 모의를 사용해야합니다.

Beck : 동의하지 않습니다. 필수 스타일 인 경우 많은 모의를 사용해야합니다. 콜 체인에서 외부 종속성이 함께 모여있는 기능적 스타일에서는 필연적이라고 생각하지 않습니다. 단위 테스트에서 많은 범위를 얻을 수 있다고 생각합니다.

그는 무엇을 의미합니까? 기능적 스타일이 어떻게 외부 종속성을 조롱하지 못하게 할 수 있습니까?



1
그들이 Java를 구체적으로 논의하고 있다면, 그 토론의 상당 부분이 무의식적이라고 생각합니다. Java는 실제로 설명되는 기능 프로그래밍에 적합하도록 지원할 필요가 없습니다. 물론 유틸리티 클래스 나 Java 8 Lambdas를 사용하여 시뮬레이션 할 수는 있지만 ... blecch.
Robert Harvey

답변:


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. 이 함수는 항상 주어진에 대해 동일한 결과를 제공하지 않습니다 usernamepassword결과는 데이터베이스에 저장된 사용자 레코드에 따라 달라집니다로 조합.
  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함수는 이제 순수 함수이므로 결과적으로 외부 사용자를 조롱 할 필요없이 핵심 사용자 인증 로직을 단위 테스트 할 수 있습니다. 이는 데이터베이스와의 상호 작용이 호출 스택에서 더 높게 처리되기 때문입니다.


해석은 명령형 스타일 = statefull 마이크로 서비스 및 기능 스타일 = stateless microservies 입니까?
k3b

@ k3b 마이크로 서비스에 대한 비트를 제외하고 일종의. 매우 단순한 명령 스타일은 상태 조작을 포함하지만 기능적 스타일은 상태 조작없이 순수한 기능을 사용합니다.
Justin

1
@ 저스틴 : 함수 스타일은 예제에서와 같이 순수한 함수와 부작용이있는 코드를 명확하게 분리한다고 말합니다. 다시 말해 기능 코드는 여전히 부작용이있을 수 있습니다.
Giorgio

실패한 시도에서 Result.FailedAttempt는 시도가 한 번 더 실패하고 순수한 함수가 수행하는 것을 제외하고는 원래 사용자와 동일한 데이터를 가진 새 사용자의 결과이므로 기능적 접근 방식은 결과 및 사용자와 쌍을 리턴해야합니다. 매개 변수로 제공되는 사용자에게 부작용을 유발합니다.
상승 어두운

이전 주석의 마지막 부분에 대한 수정 : "순수 함수 매개 변수로 제공된 사용자에게 부작용을 유발 하지 않습니다 ."
상승 Darkness
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.