다양한 상태를 나타내는 정수 코드를 반환하는 함수 재 작업


10

아래 샘플을 포함시킨 끔찍한 코드를 물려 받았습니다.

  • 이 특정 안티 패턴의 이름이 있습니까?
  • 리팩토링에 대한 몇 가지 권장 사항은 무엇입니까?

    // 0=Need to log in / present username and password
    // 2=Already logged in
    // 3=Inactive User found
    // 4=Valid User found-establish their session
    // 5=Valid User found with password change needed-establish their session
    // 6=Invalid User based on app login
    // 7=Invalid User based on network login
    // 8=User is from an non-approved remote address
    // 9=User account is locked
    // 10=Next failed login, the user account will be locked
    
    public int processLogin(HttpServletRequest request, HttpServletResponse response, 
                            int pwChangeDays, ServletContext ServContext) { 
    }
    

2
무엇인가 "확립 발견""필요 확립" ?
Tulains Córdova

4
그는해야하는데 전각 대시 "자신의 세션을 설정 유효한 사용자를 찾을 수"와 같은 읽기.
BJ Myers

2
@A_B 어떤 리턴 값이 로그인에 성공했는지는 로그인에 실패한 것입니다. 모두가 자명 한 것은 아닙니다.
Tulains Córdova

@A_B "세션 설정"은 "세션 안정화"또는 "세션 안정화 필요"를 의미합니까?
Tulains Córdova 2016

@ TulainsCórdova : "Establish"는 "적어도"(최소한이 문맥에서)만큼을 의미합니다. "따라서"세션 설정 "은"세션 작성 "과 거의 동일합니다.
hoffmale

답변:


22

이 코드는 마술 숫자 때문에뿐만 아니라 반환 코드에서 여러 가지 의미를 통합하여 그 의미 내부에 오류, 경고, 세션 생성 권한 또는 세 가지 조합을 숨겨서 의사 결정에 대한 잘못된 입력.

다음 리팩토링을 제안합니다. 가능한 결과가있는 열거 형을 반환하지만 (다른 답변에서 제안한 것처럼) 열거 형에 거부, 면제인지 여부를 나타내는 속성을 추가합니다 (이번에는 통과시켜 드리겠습니다). 괜찮다면 (통과) :

public LoginResult processLogin(HttpServletRequest request, HttpServletResponse response, 
                            int pwChangeDays, ServletContext ServContext) { 
    }

==> LoginResult.java <==

public enum LoginResult {
    NOT_LOGGED_IN(Severity.DENIAL),
    ALREADY_LOGGED_IN(Severity.PASS),
    INACTIVE_USER(Severity.DENIAL),
    VALID_USER(Severity.PASS),
    NEEDS_PASSWORD_CHANGE(Severity.WAIVER),
    INVALID_APP_USER(Severity.DENIAL),
    INVALID_NETWORK_USER(Severity.DENIAL),
    NON_APPROVED_ADDRESS(Severity.DENIAL),
    ACCOUNT_LOCKED(Severity.DENIAL),
    ACCOUNT_WILL_BE_LOCKED(Severity.WAIVER);

    private Severity severity;

    private LoginResult(Severity severity) {
        this.severity = severity;
    }

    public Severity getSeverity() {
        return this.severity;
    }
}

==> 심각도 .java <==

public enum Severity {
    PASS,
    WAIVER,
    DENIAL;
}

==> Test.java <==

public class Test {

    public static void main(String[] args) {
        for (LoginResult r: LoginResult.values()){
            System.out.println(r + " " +r.getSeverity());           
        }
    }
}

각 LoginResult의 심각도를 보여주는 Test.java의 출력 :

NOT_LOGGED_IN : DENIAL
ALREADY_LOGGED_IN : PASS
INACTIVE_USER : DENIAL
VALID_USER : PASS
NEEDS_PASSWORD_CHANGE : WAIVER
INVALID_APP_USER : DENIAL
INVALID_NETWORK_USER : DENIAL
NON_APPROVED_ADDRESS : DENIAL
ACCOUNT_LOCKED : DENIAL
ACCOUNT_WILL_BE_LOCKED : WAIVER

열거 형 값과 심각도를 기준으로 세션 생성 진행 여부를 결정할 수 있습니다.

편집하다:

@ T.Sar의 의견에 대한 응답으로 심각도의 가능한 값을 (OK, WARNING 및 ERROR) 대신 PASS, WAIVER 및 DENIAL로 변경했습니다. 그렇게하면 거부 (이전의 ERROR) 자체가 오류가 아니며 반드시 예외를 던지는 것으로 해석 되어서는 안된다는 것이 분명합니다 . 호출자는 객체를 검사하고 예외를 던질 지 여부를 결정하지만 DENIAL은 호출 결과로 유효한 결과 상태 processLogin(...)입니다.

  • 통과 : 세션이 없으면 세션을 만듭니다.
  • 웨이버 : 이번에는 계속 진행하지만 다음에 사용자는 통과 할 수 없습니다.
  • 거부 : 죄송합니다. 사용자가 통과 할 수 없으며 세션을 만들지 마십시오.

"복잡한"열거 형 (속성이있는 열거 형)을 작성하여 열거 형에 오류 수준을 포함시킬 수도 있습니다. 그러나 somme serialization 도구를 사용하면 제대로 전달되지 않을 수 있으므로주의하십시오.
Walfrat

오류 사례에서 예외를 던지고 성공을 위해 열거 형을 저장하는 것도 옵션입니다.
T. Sar

@ T.Sar 글쎄, 내가 이해했듯이 그것들은 그 자체로 오류가 아니라 어떤 이유로 세션을 만드는 것을 거부합니다. 답을 편집하겠습니다.
Tulains Córdova 2016

@ T.Sar 저는 이전에 ERROR라는 것이 유효한 상태임을 분명히하기 위해 값을 PASS, WAIVER 및 DENIAL로 변경했습니다. 어쩌면 지금은 더 나은 이름을 Severity
찾아야 할 것

나는 내 제안으로 다른 것을 생각하고 있었지만 나는 당신의 제안을 정말로 좋아했습니다! 나는 +1을 던지고있다!
T. Sar

15

이것은 원시 강박 관념 의 예입니다 . 결국 단순하지 않은 "단순한"작업에 기본 유형을 사용합니다.

이것은 bool성공 또는 실패를 표시 하기 위해 a 를 반환 한 다음 int세 번째 상태가되었을 때로 바뀌어 결국 문서화되지 않은 오류 조건의 전체 목록이 된 코드로 시작 했을 수 있습니다.

이 문제에 대한 일반적인 리팩토링은 문제의 가치를 더 잘 표현할 수있는 새로운 클래스 / 구조 / 열 / 객체 / 무엇을 생성하는 것입니다. 이 경우 enum결과 조건이 포함 된 클래스 bool또는 성공 또는 실패에 대한 클래스 , 오류 메시지, 추가 정보 등으로 전환하는 것을 고려할 수 있습니다 .

유용한 리팩토링 패턴에 대해서는 Industrial Logic의 Sfactors to Refactorings Cheatsheet를 살펴보십시오 .


7

나는 "매직 숫자"의 경우라고 부를 것이다. 그 숫자는 특별하고 그 자체로는 명백한 의미가 없다.

여기서 적용 할 리팩토링은 리턴 유형을 열거 형으로 재구성하는 것입니다. 리턴 유형은 도메인 우려를 유형으로 캡슐화하기 때문입니다. Java 열거 형을 주문하고 번호를 매길 수 있기 때문에 컴파일 오류를 처리하는 것은 다소 번거 롭습니다. 그렇지 않더라도 int로 돌아 가지 않고 직접 처리하는 것이 어렵지 않아야합니다.


그것은 일반적으로 '마법의 숫자'가 의미하는 것이 아닙니다.
D Drmmr

2
다음과 같이 콜 사이트에서 마술 번호로 표시됩니다if (processLogin(..) == 3)
Daenyth

@DDrmmr-이것은 '마법 숫자'코드 냄새가 의미하는 바입니다. 이 함수 시그니처는 processLogin ()에 "return 8;"과 같은 행이 포함되어 있음을 암시합니다. 구현 과정에서 processLogin ()을 사용하는 코드가 "if (resultFromProcessLogin == 7) {"
Stephen C. Steel

3
@Stephen 숫자의 실제 값은 여기와 관련이 없습니다. 그들은 단지 ID입니다. 매직 넘버 (Magic Numbers ) 라는 용어 는 일반적으로 의미가있는 코드의 값에 사용되지만 그 의미는 문서화되지 않습니다 (예 : 변수 이름). 여기에서 값을 명명 된 정수 변수로 바꾸면 문제가 해결되지 않습니다.
D Drmmr

2

이것은 특히 불쾌한 코드입니다. 반 패턴은 "매직 리턴 코드"로 알려져 있습니다. 여기 에서 토론을 찾을 수 있습니다 .

많은 반환 값은 오류 상태를 나타냅니다. 흐름 제어에 오류 처리를 사용할지 여부에 대한 유효한 논쟁이 있지만 귀하의 경우 성공 (코드 4), 성공이지만 암호를 변경 해야하는 코드 (코드 5) 및 "허용되지 않음"의 3 가지 경우가 있다고 생각합니다. 따라서 흐름 제어에 예외를 사용하지 않아도되는 경우 예외를 사용하여 해당 상태를 나타낼 수 있습니다.

또 다른 방법은 디자인을 리팩터링하여 성공적인 로그인을위한 "profile"및 "session"속성, 필요한 경우 "must_change_password"속성 및 로그 이유를 나타내는 많은 속성을 가진 "user"개체를 반환하는 것입니다. 플로우 인 경우 실패했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.