このアンチパターン/コードの匂いに名前はありますか?
-
09-06-2019 - |
質問
最初に断っておきますが、私はこのアプローチを支持しているわけではありません。しかし、最近このアプローチを目にし、有罪の当事者を示すために使用できる名前はないかと考えていました。それで、ここに行きます。
これでメソッドが完成したので、値を返したいとします。あなた また エラーコードを返したい。もちろん、例外を使用する方がはるかに良い選択ですが、何らかの理由で代わりにエラー コードが必要な場合があります。覚えておいてください、私はここで悪魔の代弁者を演じています。そこで、次のようなジェネリック クラスを作成します。
class FunctionResult<T>
{
public T payload;
public int result;
}
そして、次のように関数を宣言します。
FunctionResult<string> MyFunction()
{
FunctionResult<string> result;
//...
return result;
}
このパターンの 1 つのバリエーションは、エラー コードに文字列の代わりに列挙型を使用することです。さて、私の質問に戻ります。これに名前はありますか? ある場合、それは何ですか?
解決
これが特にアンチパターンではないことに同意します。使用状況によっては臭いが気になる場合があります。実際には例外を使用したくない理由があります (例:まず第一に、返されるエラーは「例外的」ではありません。
サービスに、エラーと適切な値の両方を含む結果の共通モデルを返させたい場合があります。これは、結果を例外またはその他のエラー構造に変換する低レベルのサービス インタラクションによってラップされる場合がありますが、サービスのレベルでは、サービスは例外構造を定義することなく、結果とステータス コードを返すことができます。遠隔の境界を越えて翻訳する必要があります。
このコードは必ずしもエラーであるとは限りません。HTTP 応答について考えてみましょう。HTTP 応答は、ステータス コードや応答の本文など、さまざまなデータで構成されています。
他のヒント
いわゆる "エラーコードを例外に置き換える"
まあ、それは ない アンチパターン。C++ 標準ライブラリはこの機能を利用しており、.NET では特別な機能も提供しています。 FunctionResult
.NET Framework のクラス。それは呼ばれています Nullable
. 。はい、これは関数の結果に限定されませんが、そのような場合にも使用でき、実際にここでは非常に役立ちます。.NET 1.0 にすでに Nullable
クラスでは、間違いなく次の目的で使用されていたでしょう。 NumberType.TryParse
メソッドの代わりに、 out
パラメータ。
通常、ペイロードを (const ではなく) 参照として渡し、エラー コードを戻り値として渡します。
私はゲーム開発者です、例外は排除します
Konrad の言うとおりです。C# は常に二重の戻り値を使用します。しかし、私は TryParse や Dictionary.TryGetValue などが気に入っています。C# のメソッド。
int value;
if (int.TryParse("123", out value)) {
// use value
}
の代わりに
int? value = int.TryParse("123");
if (value != null) {
// use value
}
...主な理由は、Nullable パターンが非値の戻り値の型 (つまり、クラス インスタンス) に対応しないためです。これは Dictionary.TryGetValue() では機能しません。また、TryGetValue は、KeyNotFoundException よりも優れており (デバッガー内で常に「初回例外」が発生しないため、おそらくより効率的です)、Java の get() が null を返す方法よりも優れており (null 値が予期される場合はどうなるか)、さらに効率的です。最初に ContainsKey() を呼び出します。
しかしこれは は まだ少しおかしい -- これは C# に似ているので、out パラメータを使用する必要があります。クラスをインスタンス化すると、効率の向上はすべて失われる可能性があります。
(「文字列」タイプが小文字であることを除き、Java である可能性があります。もちろん Java では、二重の戻り値をエミュレートするクラスを使用する必要があります。)
これがアンチパターンであるかどうかはわかりません。パフォーマンス上の理由から、あるいはおそらくメソッドが失敗する可能性があるという事実をより明確にするために、これが例外の代わりに使用されているのをよく見かけます。私にとって、これはアンチパターンというよりも個人的な好みのようです。
これはアンチパターンではないと言う人たちに私は同意します。これは、特定のコンテキストでは完全に有効なパターンです。例外となるのは、 例外的な 状況では、戻り値(例のように)は予想される状況で使用する必要があります。一部のドメインはクラスからの有効な結果と無効な結果を期待しており、どちらも例外としてモデル化すべきではありません。
たとえば、ガソリンの量が X の場合、車は A から B まで移動できますか? 移動できる場合、ガソリンはどのくらい残っていますか?この種の質問は、提供されたデータ構造にとって理想的です。A から B への移動ができないことは予想されるため、例外を使用すべきではありません。
このアプローチは、私がこれまでに見た他のアプローチよりもはるかに優れています。たとえば、C の一部の関数は、エラーが発生すると戻り、成功したように見えます。失敗したことを確認する唯一の方法は、最新のエラーを取得する関数を呼び出すことです。
MacBook でセマフォ コードをデバッグしようとして何時間も費やしましたが、最終的に sem_init が OSX では動作しないことがわかりました。エラーなしでコンパイルされ、エラーも発生せずに実行されましたが、セマフォが機能せず、その理由がわかりませんでした。を使用するアプリケーションを移植した人々が残念です POSIX セマフォ OSX に移行し、すでにデバッグされているリソース競合の問題に対処する必要があります。
「エラーかどうか判断できない」パターンはどうでしょうか。本当に例外が発生したが、部分的な結果を返したい場合は、結果を例外にラップすることになるようです。
例外を使用したくない場合、最もクリーンな方法は、関数にエラー/成功コードを返し、結果が入力される参照引数またはポインタ引数を受け取ることです。
それをアンチパターンとは呼びません。これは非常によく実証された実行可能な方法であり、多くの場合、例外を使用するよりも推奨されます。
メソッドが時々失敗することが予想されるが、それが例外的であるとは考えない場合は、.NET Framework で使用されているこのパターンを好みます。
bool TryMyFunction(out FunctionResult result){
//...
result = new FunctionResult();
}
匂いやアンチパターンに関する議論は、島からお互いに投票しようとするさまざまな番組構造を持ったテレビ番組「サバイバー」を思い出させます。私は、何をすべきか、何をすべきではないかという継続的に進化する命令のリストよりも、「構築 X にはこれこれの長所と短所がある」ということを知りたいのです。
アンチパターン指定を擁護するために、このコードはいくつかの方法で使用されるのに適しています。
- オブジェクト x = MyFunction().payload; (戻り結果を無視 - 非常に悪い)
- int コード = MyFunction().result; (ペイロードを破棄します - それが意図された用途であれば問題ないかもしれません。)
- FunctionResult x = MyFunction();//... (追加の FunctionResult オブジェクトと、それらをあちこちでチェックするための追加のコードの束)
リターンコードを使用する必要がある場合は、問題ありません。ただし、リターンコードを使用してください。余分なペイロードを詰め込もうとしないでください。それが何です 参照 そして 外 パラメータ(C#)用です。Null 許容型は例外かもしれませんが、それをサポートするために言語に追加のシュガーが組み込まれているからです。
それでもこの評価に同意できない場合は、この回答に反対票を投じてください (質問全体ではありません)。それがアンチパターンだと思う場合は、賛成票を投じてください。この回答を使用して、コミュニティの意見を確認します。