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


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
何をしている「を確立-た」「必要な確立」
TulainsCórdova17年

4
これはemダッシュであるはずです。「有効なユーザーが見つかりました:セッションを確立してください」のように読んでください。
BJマイヤーズ2017年

2
@A_Bログインが成功した場合の戻り値は、ログインが失敗した場合です。すべてが自明であるとは限りません。
TulainsCórdova17年

@A_B「セッションの確立」は「セッションの確立」または「セッションの確立が必要」を意味しますか?
TulainsCórdova17年

@TulainsCórdova:「確立する」とは(少なくともこのコンテキストでは)「作成する」ことを意味します。つまり、「セッションを確立する」は「セッションを作成する」とほぼ同じです
hoffmale

回答:


22

マジックナンバーが原因でコードが悪いだけでなく、エラー、警告、セッションを作成する権限、または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(...)

  • 合格:セッションがまだない場合は、セッションを作成してください。
  • 権利放棄:今回は先に進みますが、次回ユーザーは合格することができない場合があります
  • 拒否:申し訳ありませんが、ユーザーはパスできず、セッションを作成しません

「複雑な」列挙型(属性付きの列挙型)を構築して、エラーのレベルを列挙型に埋め込むこともできます。ただし、sommeシリアライゼーションツールを使用すると、あまりうまくいかない場合があるので注意してください。
Walfrat 2017年

エラーの場合に例外をスローし、列挙のみを保存して成功させることもオプションです。
T. Sar

@ T.Sarまあ、私が理解しているように、それ自体はエラーではなく、何らかの理由でセッションを作成することを拒否しています。回答を編集します。
TulainsCórdova17年

@ T.Sar値をPASS、WAIVER、DENIALに変更して、以前にERRORと呼んでいたものを有効なステータスであると明確にしました。多分今私はのためのよりよい名前を思い付くべきですSeverity
TulainsCórdovaJun

私は私の提案で何か他のものを考えていましたが、私はあなたの提案が本当に好きでした!確かに+1を投げています!
T. Sar

15

これはプリミティブオブセッションの例です。「単純な」タスクにプリミティブ型を使用すると、最終的にはそれほど単純ではなくなります。

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

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

役立つかもしれないより多くのリファクタリングパターンについては、Industrial LogicのSmells to Refactorings Cheatsheetをご覧ください


7

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

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


それは通常「マジックナンバー」で意味されるものではありません。
D Drmmr 2017年

2
コールサイトではマジックナンバーとして表示されます。例:if (processLogin(..) == 3)
Daenyth

@DDrmmr-これはまさに「マジックナンバー」コードの匂いが意味するものです。この関数のシグネチャは、ほぼ確実に、processLogin()に「return 8;」のような行が含まれていることを意味します。その実装では、ProcessLogin()を使用するコードが「if(resultFromProcessLogin == 7){」のように見えるように強制します。
スティーブンC.スチール

3
@Stephen数値の実際の値はここでは無関係です。それらは単なるIDです。マジックナンバーという用語は通常、意味のあるコード内の値に使用されますが、誰の意味が文書化されていません(変数名など)。ここで値を名前付き整数変数に置き換えても問題は解決しません。
D Drmmr 2017年

2

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.