私は一見単純なシナリオを持っており、単純な解決策が必要ですが、どちらが「最も正しい」か「ほとんどのJava」かは明らかではありません。
あるクラスに小さな認証(クライアントクライアント)メソッドがあるとしましょう。認証はいくつかの理由で失敗する可能性があり、制御フローに対して単純なブール値を返したいだけでなく、ユーザーに対して文字列メッセージも返したいと思います。これらは私が考えることができる可能性です:
他に何か提案はありますか?
編集このオプションを見逃しました
編集解決策:
私はほとんどのOOソリューションを選び、小さなAuthenticationResultクラスを作成しました。これは他の言語では行いませんが、Javaでは好きです。文字列を返すという提案も好きでした。 [] nullリターンに似ていますが、より安全です。Resultクラスの利点の1つは、必要に応じて詳細を含む成功メッセージを表示できることです。
ブールフラグと文字列の両方が含まれている小さなオブジェクトを返すことは、おそらく最もOOに似た方法ですが、このような単純なケースではやり過ぎに見えることに同意します。
もう1つの方法は、常に文字列を返し、null(または空の文字列-どちらかを選択)で成功を示すことです。戻り値がjavadocsで明確に説明されている限り、混乱することはありません。
例外を使用できます。
try {
AuthenticateMethod();
} catch (AuthenticateError ae) {
// Display ae.getMessage() to user..
System.out.println(ae.getMessage());
//ae.printStackTrace();
}
次に、AuthenticateMethodでエラーが発生した場合、新しいAuthenticateErrorを送信します(例外を拡張します)
「センチネル値」、特にnullを返すことは避けてください。実装を読まないと呼び出し元がメソッドを理解できないコードベースになってしまいます。 nullの場合、呼び出し元は、メソッドがnullを返す可能性があることを忘れた(または知らない)と、NullPointerExceptionsになってしまう可能性があります。
Bas LeijdekkersからのTupleの提案は、メソッドから複数の値を返したい場合に常に使用する良い提案です。私たちが使用しているのは P2<A, B>
Functional Java ライブラリです。この種のタイプは、他の2つのタイプの結合です(各タイプの値が1つ含まれています)。
制御フローの例外のスローはコードの臭いですが、チェックされた例外は、メソッドから複数のタイプの値を取得する1つの方法です。しかし、他のよりクリーンな可能性が存在します。
Option<T>
抽象クラスを2つのサブクラスSome<T>
とNone<T>
で持つことができます。これは、nullのタイプセーフな代替手段に少し似ており、部分関数(一部の引数に対して戻り値が定義されていない関数)を実装するための優れた方法です。 Functional Java ライブラリには、Iterable<T>
を実装するフル機能のOption
クラスがあるため、次のようなことができます。
public Option<String> authenticate(String arg) {
if (success(arg))
return Option.some("Just an example");
else
return Option.none();
}
...
for(String s : authenticate(secret)) {
privilegedMethod();
}
または、 Either<L, R>
クラスとして、2つのタイプの非交和を使用することもできます。タイプL
またはR
のいずれかの値が1つ含まれています。このクラスは、L
とR
の両方にIterable<T>
を実装しているため、次のように実行できます。
public Either<Fail, String> authenticate(String arg) {
if (success(arg))
return Either.right("Just an example");
else
return Either.left(Fail.authenticationFailure());
}
...
Either<Fail, String> auth = authenticate(secret);
for(String s : auth.rightProjection()) {
privilegedMethod();
}
for(Fail f : auth.leftProjection()) {
System.out.println("FAIL");
}
これらのクラス、P2
、Option
、およびEither
はすべて、さまざまな状況で役立ちます。
その他のオプション:
単純なタプルの例では、実際の実装にはさらに多くのことが必要になる場合があります。
class Tuple<L, R> {
public final L left;
public final R right;
public Tuple( L left, R right) {
this.left = left;
this.right = right;
}
}
認証の失敗が一般的であるからといって、それが例外ではないという意味ではありません。
私の意見では、認証の失敗は、チェックされた例外のposter-childユースケースです。 (まあ...おそらくファイルが存在しないことが標準的なユースケースですが、認証の失敗は近い#2です。)
問題がなかったことを示す空のエラーメッセージのコレクションを返すことができます。これは、3番目の提案を改良したものです。
個人的には、ブール値と文字列を使用してAuthenticationStatusという新しいクラスを作成するのが最もJavaのような方法だと思います。やり過ぎのように見えますが(おそらくそうかもしれませんが)、私にはわかりやすく、理解しやすい。
認証の成功は「通常の」ケースである必要があるため、認証の失敗は例外的なケースです。
とにかく、ユーザーのさまざまなステータス文字列は何ですか。成功と失敗の2つしか見えません。それ以上の情報は潜在的なセキュリティ問題です。例外を除いたソリューションのもう1つの利点は、間違った方法で呼び出すことができず、失敗のケースがより明白になることです。例外なく、次のように記述します。
if (authenticate()) {
// normal behaviour...
}
else {
// error case...
}
戻り値を無視して、誤ってメソッドを呼び出す可能性があります。次に、「通常の動作」コードが認証に成功せずに実行されます。
authenticate();
// normal behaviour...
例外を使用する場合、それは起こり得ません。例外を使用しないことにした場合は、少なくともメソッドに名前を付けて、状態を返すことが明確になるようにします。例:
if (isAuthenticated()) {
//...
}
ここには良い答えがたくさんあるので、短くしておきます。
ユーザーの認証の失敗は、チェックされた例外の有効なケースと見なすことができると思います。あなたのプログラミングスタイルが例外の処理を好むなら、これをしない理由はありません。また、「メソッドから複数の値を返す方法、私のメソッドは1つのことを実行します。ユーザーを認証します」も削除します。
複数の値を返す場合は、10分かけて汎用のPairTupleを作成し(ペアのTripleTupleを超えることもあります。上記の例は繰り返しません)、その方法で値を返します。小さなdtoスタイルのオブジェクトがさまざまな複数の値を返すのは嫌いです。
私は自分で「小さなクラス」を使用しますが、通常は内部クラスを使用します。メッセージを収集するために引数を使用するのは好きではありません。
また、失敗する可能性のあるメソッドが「低レベル」である場合(アプリサーバーやデータベースレイヤーからの場合など)、戻りステータスを含むEnumを返し、それをGUIレベルの文字列に変換することをお勧めします。コードを国際化する場合は、ユーザー文字列を低レベルで渡さないでください。アプリサーバーは、異なるクライアントを異なる言語で動作させるのではなく、一度に1つの言語でしか応答できません。
これは他のプログラミング言語では一般的なイディオムのようですが、どれがどれかわかりません(質問で読んだCだと思います)。
1つの関数から2つの値を返そうとすると、誤解を招く可能性があります。しかし、そうしようとする試みによって証明されているので、それも非常に役立つかもしれません。
以前に投稿されたように、それがアプリの一般的なフローである場合は、確実に作成し、結果を含む小さなクラスを続行する正しい方法である必要があります。
関数から2つの値を返すことについての引用は次のとおりです。
プログラミングスタイルの問題として、このアイデアはオブジェクト指向プログラミング言語では魅力的ではありません。計算結果を表すためにオブジェクトを返すことは、複数の値を返すためのtheイディオムです。無関係な値のクラスを宣言する必要はないが、単一のメソッドから無関係な値を返す必要はないという意見もあります。
Javaを許可するための機能リクエストでそれを見つけました 複数の戻り値
日付の「評価」セクションを見てください:2005-05-06 09:40:08
これはあなたがそのような要件を持っている唯一の方法ですか?そうでない場合は、isSuccessfulフラグとメッセージ文字列を使用して一般的なResponseクラスを生成し、それをあらゆる場所で使用します。
または、メソッドにnullを返して成功を示すこともできます(きれいではなく、成功とメッセージを返すことはできません)。
私はおそらく次のようなものに行きます:
class SomeClass {
public int authenticate (Client client) {
//returns 0 if success otherwise one value per possible failure
}
public String getAuthenticationResultMessage (int authenticateResult) {}
//returns message associated to authenticateResult
}
この「設計」では、認証が失敗した場合にのみメッセージを要求できます(これは、99,99%の確率で発生するシナリオです;))
メッセージ解決を別のクラスに委任することも良い習慣かもしれません。ただし、アプリケーションのニーズによって異なります(ほとんどの場合、i18nが必要ですか?)