web-dev-qa-db-ja.com

多くの異なるステータスを表す整数コードを返す関数を作り直す

私は以下の短いサンプルを含めたいくつかのひどいコードを継承しました。

  • この特定のアンチパターンに名前はありますか?
  • これをリファクタリングするための推奨事項は何ですか?

    // 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) { 
    }
    
10
A_B

マジックナンバー であるだけでなく、エラー、警告、セッションを作成するためのアクセス許可またはその組み合わせの意味の中に隠れて、戻りコードのいくつかの意味が合体しているため、コードは悪いです3つは意思決定の悪い入力になります。

私は次のリファクタリングをお勧めします:可能な結果を​​含む列挙型を返します(他の回答で提案されているように)が、拒否であるか、権利放棄であるかを示す属性を列挙型に追加します(これを最後に渡します)またはOK(パス)の場合:

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

==> 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に変更しました。そうすれば、DENIAL(以前はERROR)自体はエラーではなく、必ずしも例外で例外をスローすることにはならないはずです。呼び出し元はオブジェクトを調べて、例外をスローするかどうかを決定しますが、DENIALはprocessLogin(...)を呼び出した結果の有効な結果ステータスです。

  • PASS:先に進み、まだセッションがない場合は作成します
  • WAIVER:今回は先に進みますが、次にユーザーがパスすることを許可されない可能性があります
  • DENIAL:申し訳ありませんが、ユーザーはパスできず、セッションを作成しません
22

これはPrimitive Obsessionの例です-最終的にそれほど単純ではなくなる「単純な」タスクにプリミティブ型を使用します。

これは、成功または失敗を示すためにboolを返し、3番目の状態になるとintに変わって、最終的に文書化されていないエラーの完全なリストになったコードとして始まった可能性があります条件。

この問題の典型的なリファクタリングは、問題の値をより適切に表すことができる新しいクラス/構造体/列挙型/オブジェクト/何でも作成することです。この場合、結果条件を含むenumに切り替えることを検討するか、成功または失敗、エラーメッセージ、追加情報などのためにboolを含む可能性があるクラスに切り替えることを検討してください。

役立つ可能性のあるその他のリファクタリングパターンについては、Industrial Logicの Smells to Refactorings Cheatsheet を参照してください。

15
BJ Myers

私はそれを「マジックナンバー」のケースと呼びます-特別であり、それ自体には明らかな意味がない数字です。

ここで適用するリファクタリングは、戻り値の型を列挙型に再構成することです。これは、ドメインの問題を型にカプセル化するためです。 Java enumは順序付けして番号を付けることができるので、そこから抜け出すコンパイルエラーに対処することは可能です。そうでない場合でも、落ちるのではなく、直接対処することは難しくありません。 intに戻る。

7
Daenyth

これは特に不愉快なコードです。アンチパターンは「マジックリターンコード」として知られています。ディスカッション here を見つけることができます。

戻り値の多くはエラー状態を示しています。フロー制御にエラー処理を使用するかどうかについて有効な debate がありますが、あなたの場合、3つのケースがあると思います:成功(コード4)、成功だがパスワードを変更する必要がある(コード5)、および「許可されていません」。したがって、フロー制御に例外を使用する必要がない場合は、例外を使用してそれらの状態を示すことができます。

別のアプローチは、設計をリファクタリングして、成功したログインの「プロファイル」および「セッション」属性、必要に応じて「must_change_password」属性、およびログの理由を示す一連の属性を含む「ユーザー」オブジェクトを返すことです。それがフローの場合、-inは失敗しました。

2
Neville Kuyt