5

私は有用な回答の多くを統合し、以下の独自の回答を考え出しました


たとえば、Foo明示的な初期化と終了が必要な API を作成しています。(言語に依存しないはずですが、ここでは C++ を使用しています)

class Foo
{
public:
    static void InitLibrary(int someMagicInputRequiredAtRuntime);
    static void TermLibrary(int someOtherInput);
};

どうやら、私たちのライブラリはマルチスレッドや再入可能性などを考慮していないようです。関数を 1 回だけ呼び出す必要があるとしましょうInit。他の入力で再度呼び出すと、大混乱が生じます。

これを発信者に伝える最善の方法は何ですか? 次の 2 つの方法が考えられます。

  1. 内部InitLibraryではassert、呼び出し元を 2 回初期化したと非難するいくつかの静的変数があります。
  2. 内部InitLibraryで、いくつかの静的変数をチェックし、ライブラリがすでに初期化されている場合は黙って中止します。

方法 1 は明らかに明示的ですが、方法 2 の方がユーザーフレンドリーです。メソッド#2には、呼び出し元がInitLibrary2回呼び出されるべきではないという事実に気付かないという欠点があると考えています。

各アプローチの長所/短所は何ですか? これらすべてを覆すより賢い方法はありますか?

編集

ここでの例は非常に不自然であることを知っています。@daemonが指摘したように、私は自分自身を初期化する必要があり、呼び出し元に迷惑をかけません。ただし、実際には、自分自身を適切に初期化するためにさらに情報が必要な場所があります ( my variable name の使用に注意してくださいsomeMagicInputRequiredAtRuntime)。これは初期化/終了に限定されませんが、引用と引用の「耐障害性」を選択するか、ひどく失敗するかのジレンマが存在する他の例です。

4

9 に答える 9

9

わかりやすい例外、これが失敗する理由を説明する適切なドキュメントとともに、私は間違いなくアプローチ 1を使用します。これにより、呼び出し元はこれが発生する可能性があることを認識し、呼び出し元のクラスは、必要に応じて呼び出しを try-catch ステートメントで簡単にラップできます。

一方、黙って失敗すると、ユーザーは 2 番目の呼び出しが成功した (エラー メッセージも例外もない) と信じ込み、新しい値が設定されていることを期待するようになります。そのため、 で何か他のことをしようとしてもFoo、期待した結果が得られません。そして、彼らがあなたのソース コードにアクセスできない場合、その理由を突き止めることはほぼ不可能です。

于 2010-07-21T13:18:16.157 に答える
4

Serenity Prayer (インターフェイス用に変更)

     SA,  grant me the assertions 
     to accept the things devs cannot change 
     the code to except the things they can, 
     and the conditionals to detect the difference

障害が環境にある場合は、コードでそれを処理するようにしてください。開発者がコードを修正することで防止できる場合は、例外を生成する必要があります。

于 2010-07-21T13:32:19.450 に答える
3

適切なアプローチは、初期化されたライブラリ オブジェクトを作成するファクトリを用意することです (これには、ライブラリをクラスでラップする必要があります)。ファクトリへの複数の create-call は、異なるオブジェクトを作成します。このようにするinitializeと、メソッドはライブラリのパブリック インターフェイスの一部ではなくなり、ファクトリが初期化を管理します。

ライブラリのインスタンスが 1 つしかアクティブにならない場合は、ファクトリで既存のインスタンスをチェックします。これにより、効果的にライブラリ オブジェクトがsingletonになります。

于 2010-07-21T13:21:49.930 に答える
2

ルーチンが期待される事後条件を達成できない場合は、例外にフラグを立てることをお勧めします。誰かが init ルーチンを 2 回呼び出し、2 回目に呼び出した後のシステム状態が 1 回呼び出された場合と同じになる場合、おそらく例外をスローする必要はありません。2 回目の呼び出し後のシステム状態が呼び出し元の期待と一致しない場合は、例外をスローする必要があります。

一般に、アクションという観点よりも、状態という観点から考える方が役立つと思います。類似性を使用するために、既に開いているファイルを「新規書き込み」として開こうとすると、失敗するか、閉じる-消去する-再度開く必要があります。プログラムは、作成時刻が現在の時刻と一致する空のファイルに書き込むことを想定しているため、単純にノーオペレーションを実行するべきではありません。一方、既に閉じられているファイルを閉じようとしても、通常はエラーと見なされません。これは、ファイルを閉じることが目的であるためです。

ところで、例外をスローする可能性のあるメソッドの "Try" バージョンを利用できるようにしておくと役立つことがよくあります。たとえば、更新ルーチンなどで Control.TryBeginInvoke を使用できると便利です (スレッド セーフなコントロール プロパティが変更された場合、プロパティ ハンドラーはコントロールがまだ存在する場合は更新することを望んでいますが、実際にはそうしません)。プロパティが更新されているときにコントロールが閉じられた場合、最初のチャンスの例外を回避できないのは少し面倒です)。

于 2010-07-21T15:05:47.877 に答える
1

このジレンマを覆す 1 つの方法は、両方の陣営を満たすことだと思います。Ruby には-w警告スイッチがあります。これは gcc ユーザー向けのカスタムで-Wallあり-Weffc++、Perl にはテイント モードがあります。デフォルトでは、これらは「正常に機能します」が、より注意深いプログラマーは、これらの厳密な設定を自分でオンにすることができます。

「常にわずかなエラーでも文句を言う」というアプローチに反対する 1 つの例は、HTML です。すべてのブラウザーが CSS ハッキング (負の座標で要素を描画するなど) に吠えたら、世界がどれほど不満を感じるか想像してみてください。

多くの優れた回答を検討した後、私は次の結論に達しました。誰かが座っているとき、私の API は理想的には「機能する」べきです。もちろん、誰もが何らかのドメインに関与するには、解決しようとしている問題よりも 1 つまたは 2 つの抽象レベルで作業する必要があります。つまり、ユーザーは遅かれ早かれ私の内部について学ぶ必要があります。彼が私の API を十分に長く使用すると、限界を広げ始め、内部の仕組みを「隠す」または「カプセル化」するための過度の努力は迷惑になるだけです。

ほとんどの場合、フォールト トレランスは良いことだと思いますが、API ユーザーが特殊なケースを拡大している場合、それを正しく行うのは難しいというだけです。両方の世界の最善の方法は、ある種の「厳密なモード」を提供することであり、物事が「正常に機能しない」場合に、ユーザーが問題を簡単に分析できるようにすることです。

もちろん、これを行うには多くの余分な作業が必要になるため、ここでは理想について話しているだけかもしれません。実際には、すべては特定のケースとプログラマーの決定に帰着します。

于 2010-07-23T01:38:23.813 に答える
1

クラスにプライベート静的カウンター変数を用意します。0 の場合は、Init でロジックを実行し、カウンターをインクリメントします。0 より大きい場合は、単にカウンターをインクリメントします。Term では反対のことを行い、0 になるまでデクリメントしてからロジックを実行します。

もう 1 つの方法は、Singleton パターンを使用することです。これは C++ のサンプルです。

于 2010-07-21T13:21:38.207 に答える
0

言語でこのエラーが静的に表示されない場合、実行時にのみエラーが表示される可能性が高くなります。ライブラリの使用状況にもよりますが、これは、開発のかなり後になるまでエラーが表面化しないことを意味します。おそらく出荷時のみです(繰り返しますが、多くの場合に依存します).

エラーを黙って食べても危険がなければ (危険なことが起こる前にそれをキャッチするので、実際のエラーではありません)、黙って食べるべきだと思います。これにより、よりユーザーフレンドリーになります。

ただしsomeMagicInputRequiredAtRuntime、呼び出しごとに異なる場合は、可能な限りエラーを発生させるか、ライブラリが期待どおりに機能しない可能性があります (「値 42 でライブラリを初期化しましたが、11 で初期化したかのように動作しています!?」 )。

于 2010-07-21T13:21:00.503 に答える
0

このライブラリが静的クラス (状態を持たないライブラリ型) である場合Init、型初期化子に呼び出しを入れてみませんか? インスタンス化可能な型の場合は、コンストラクターまたはインスタンス化を処理するファクトリ メソッドに呼び出しを配置し​​ます。関数
へのパブリック アクセスを許可しないでください。Init

于 2010-07-21T13:22:02.380 に答える
0

あなたのインターフェースは少し技術的すぎると思います。API の設計中に使用した概念を知りたいプログラマーはいません。プログラマーは、実際の問題に対する解決策を求めており、API の使用方法を学びたいとは思っていません。誰もあなたの API を初期化したくありません。これは、API が可能な限りバックグラウンドで処理する必要があるものです。開発者を低レベルの技術的なものからできるだけ保護する適切な抽象化を見つけてください。つまり、API はフォールト トレラントである必要があります。

于 2010-07-21T13:34:49.733 に答える