問題タブ [api-design]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
idioms - API 設計: 「フォールト トレランス」は良いことですか?
私は有用な回答の多くを統合し、以下の独自の回答を考え出しました
たとえば、Foo
明示的な初期化と終了が必要な API を作成しています。(言語に依存しないはずですが、ここでは C++ を使用しています)
どうやら、私たちのライブラリはマルチスレッドや再入可能性などを考慮していないようです。関数を 1 回だけ呼び出す必要があるとしましょうInit
。他の入力で再度呼び出すと、大混乱が生じます。
これを発信者に伝える最善の方法は何ですか? 次の 2 つの方法が考えられます。
- 内部
InitLibrary
ではassert
、呼び出し元を 2 回初期化したと非難するいくつかの静的変数があります。 - 内部
InitLibrary
で、いくつかの静的変数をチェックし、ライブラリがすでに初期化されている場合は黙って中止します。
方法 1 は明らかに明示的ですが、方法 2 の方がユーザーフレンドリーです。メソッド#2には、呼び出し元がInitLibrary
2回呼び出されるべきではないという事実に気付かないという欠点があると考えています。
各アプローチの長所/短所は何ですか? これらすべてを覆すより賢い方法はありますか?
編集
ここでの例は非常に不自然であることを知っています。@daemonが指摘したように、私は自分自身を初期化する必要があり、呼び出し元に迷惑をかけません。ただし、実際には、自分自身を適切に初期化するためにさらに情報が必要な場所があります ( my variable name の使用に注意してくださいsomeMagicInputRequiredAtRuntime
)。これは初期化/終了に限定されませんが、引用と引用の「耐障害性」を選択するか、ひどく失敗するかのジレンマが存在する他の例です。
java - タスクがRuntimeException/Errorをスローした場合、ScheduledExecutorService.scheduleAt *メソッドはタスクを再スケジュールする必要がありますか?
先日、アプリケーションに重要なサービスを実装していましたが、それは何があっても実行し続けるはずです。そこで、次の構成を使用しました。
...importantPeriodicTaskがRuntimeExceptionまたはErrorを実際にスローすると、ScheduledExecutorService
はこのタスクの実行を停止します(スケジュールされなくなります)。
もちろん、これはまさにjavadocが言っていることです。
タスクの実行で例外が発生した場合、それ以降の実行は抑制されます。
恥ずかしいのですが、なぜ作者ScheduledExecutorService
がこのように実装したのか理解できませんでした。
確かに、RuntimeExceptionまたはError、特にErrorは通常はキャッチされません。しかし実際には、特にRuntimeExceptionの場合、実際のデプロイメントでは非常に一般的にスローされます。その特定の操作は失敗するはずですが、その孤立したエラーのためにアプリ自体が失敗しないことがほとんどの場合望ましいと思います。
ある周期的なタスクの抑制が他の種類の周期的なタスクに影響を与えないのは事実です。しかし、ほとんどの定期的なタスクの性質を考えると、これらのタスクは、孤立したタスクではなく、「サービス」として認識されるべきではありませんか?
つまり、その1つのインスタンスだけがimportantPeriodicTask
失敗し、タスク自体のスケジュールが変更され続けるべきではないでしょうか。
c++ - プライベートクラス関数と名前のない名前空間の関数
私はプライベートクラスの関数を持たない傾向があることに気づきました。可能であれば、プライベートクラス関数のすべての候補者は、名前のない名前空間に入れて、必要なすべての情報を関数パラメーターとして渡します。なぜそうしているのか、はっきりとした説明はありませんが、少なくとも私にはもっと自然に見えます。結果として、ヘッダーファイルで公開する内部の詳細を少なくする必要があります。
あなたの意見は何ですか-それは正しい習慣ですか?
java - 流暢さを促進するために引数を返すように void メソッドを改良する: 破壊的な変更?
「API の設計はセックスのようなものです。1 つの間違いを犯すと、残りの人生をサポートすることができます」 ( Twitter の Josh Bloch )
Java ライブラリには多くの設計ミスがあります。Stack extends Vector
(ディスカッション)、破損を引き起こさずにそれを修正することはできません。廃止しようとすることはできますがInteger.getInteger
(議論)、おそらく永遠に残るでしょう。
それにもかかわらず、特定の種類のレトロフィットは、破損することなく行うことができます。
効果的な Java 2nd Edition、項目 18: 抽象クラスよりもインターフェイスを優先する: 既存のクラスは、新しいインターフェイスを実装するために簡単に改造できます。
例: String implements CharSequence
、Vector implements List
など
有効な Java 2nd Edition、項目 42: varargs を慎重に使用する: 既存のクライアントに影響を与えずに、最終パラメーターとして配列を受け取る既存のメソッドを改造して、代わりに varags を使用できます。
(悪名高い) 有名な例はArrays.asList
、混乱 (議論) を引き起こしましたが、破損は引き起こしませんでした。
この質問は、別の種類の改造に関するものです。
void
既存のコードを壊さずに何かを返すようにメソッドを改良できますか?
私の最初の予感はイエスです。
- 戻り値の型は、コンパイル時に選択されるメソッドには影響しません
- 参照: JLS 15.12.2.11 - 戻り型は考慮されない
- したがって、戻り値の型を変更しても、コンパイラによって選択されるメソッドは変更されません。
- を改造し
void
て何かを返すことは合法です (ただし、その逆はできません!)
- リフレクションを使っても
Class.getMethod
返り値の型が区別されないなど
ただし、Java/API 設計の経験が豊富な他の人による、より徹底的な分析を聞きたいと思っています。
付録: 動機
タイトルで示唆されているように、1 つの動機は流暢なインターフェイススタイルのプログラミングを容易にすることです。
シャッフルされた名前のリストを出力するこの単純なスニペットを考えてみましょう:
入力リストを返すCollections.shuffle(List)
ように宣言されていた場合、次のように記述できます。
の代わりに、などCollections
の入力リストを返すと、はるかに快適に使用できるメソッドが他にもあります。実際、とreturnを持つことは特に不幸です。このように:void
reverse(List)
sort(List)
Collections.sort
Arrays.sort
void
もちろん、流暢さを妨げているこのvoid
戻り値の型は、これらのユーティリティ メソッドだけに限定されているわけではありません。メソッドは、流暢さを促進するために(alaおよび) をjava.util.BitSet
返すように作成することもできます。this
StringBuffer
StringBuilder
残念ながら、StringBuilder
/とは異なりStringBuffer
、すべてのBitSet
ミューテーターは を返しvoid
ます。
関連トピック
api-design - Webサイトが独自のAPIを使用することは良い習慣ですか?
サイト自体が実際にAPIを使用するように、サイトの開発中にAPIを開発することは良い習慣ですか?または、これを選択した場合、パフォーマンスが低下しますか?
たとえば、FacebookやDiggなどの成熟したサイトが独自のAPIを使用してCRUD(作成、読み取り、更新、削除)を行っているかどうか、または独自のバックエンドを持っているかどうかを誰かが知っていますか?ありがとう
c - シリアルポートに接続されたデバイスと通信するためのAPIの作成
私の質問のいくつかの用語が間違っているのではないかと心配しています。私に耐えて、私が間違っているところはどこでも私を訂正してください。
シリアルポートに接続されたカードリーダーを操作するための一連の機能を提供するライブラリ/プログラムを作成する必要があります。挿入されたカードを取り出すのと同じように、ユーザーは自分のコードを呼び出すだけです。たとえば、
cardEject()
その他の機能は、シリアルポートのオープン、データの書き込み、確認応答の確認、エラーコードの確認、障害が発生した場合のコマンドの再送信などを自動的に処理します。シリアルポート上のデバイスとの通信についてはかなり明確です。
私の質問は、これらすべての関数を記述してテストした後、どのようにユーザーに提供する必要があるかということです。
彼にヘッダーファイル(.h
)とオブジェクトファイル(.o
)を与える必要がありますか?実際のプログラムをコンパイルしながらオブジェクトにリンクできるようにします。
静的ライブラリ(.a
)を提供する必要がありますか?
どちらが良いアイデアですか?
各機能がシリアルポートを開いてから閉じるのは良い考えですか?または、initCardReader()
それを開き、そのプロパティを設定し、それcloseCardReader()
を閉じる必要がありますか?initCardReader()
他のすべての関数は、 ?の後にのみ呼び出すことができます。
今、ばかげているが本当の質問:-)そのようなプログラムに使用される用語は何ですか?それはドライバーまたはライブラリまたはデバイスインターフェイスですか?そのようなプロジェクトの正しいラベルは何ですか?
御時間ありがとうございます。
編集
私を導いてくれた皆さんに感謝します。本当に感謝。
このAPIは、より大きなプロジェクトの一部になる必要があります。実際、私もそのプロジェクトに取り組んでいます。しかし、このAPIは、私がいるかどうかに関係なく、他のプロジェクトで使用される可能性が高いです。他のプロジェクトでの使用の可能性を考えると、ライブラリの方が理にかなっていると思います。私が間違っている場合は、親切に訂正してください。
java - アノテーションを使用して、メソッドによって返された値が破棄されないようにする
String
Java では不変です。次のスニペットは、大まかに言えば「間違っています」。
これは「間違っている」にもかかわらず、コードはコンパイルされて実行されます。おそらく多くの初心者は、間違いが何であるかを説明するか、ドキュメントを参照して自分で調べる必要があるため、混乱します。
ドキュメントを読むことは API を理解する上で不可欠ですが、コンパイル時のチェックを追加することでこれを補うことができるかどうか疑問に思っています。特に、Java の注釈フレームワークを使用して、特定のメソッドによって返される値が無視されないようにすることができるのではないかと考えています。API 設計者/ライブラリ作成者は、メソッドでこの注釈を使用して、どの戻り値を無視してはならないかを文書化します。
API がこの注釈 (またはおそらく別のメカニズム) で補足されると、ユーザーが上記のようなコードを記述しても、コンパイルされません (または厳しい警告が表示されます)。
それで、これを行うことができますか?
付録: 動機
一般的なケースでは、Javaがメソッドの戻り値を無視できるようにする必要があることは明らかです。List.add
( always true
)、 (previous value)などのメソッドの戻り値はSystem.setProperty
、ほとんどの場合、おそらく安全に無視できます。
ただし、戻り値を無視してはならないメソッドも多数あります。そうすることは、ほとんどの場合、プログラマーのエラーであるか、API の適切な使用法ではありません。これらには次のようなものが含まれます。
- 呼び出されたインスタンスを変更するのではなく、操作の結果を返す不変型 (
String
、BigInteger
など) のメソッド。 - 戻り値が動作の重要な部分であり、無視されるべきではないメソッドですが、とにかく無視する人もいます (たとえば
InputStream.read(byte[])
、読み取られたバイト数を返します。これは、配列の全長であると想定されるべきではありません)。
現在、これらの戻り値を無視するコードを記述して、警告なしでコンパイルおよび実行することができます。静的分析チェッカー / バグ ファインダー / スタイル エンフォーサー / などは、ほぼ確実にこれらをコードの匂いの可能性としてフラグを立てることができますが、これが API 自体によって、おそらく注釈を通じて強制される場合は、適切/理想的であるように思われます。
クラスが常に「適切に」使用されることを保証することはほとんど不可能ですが、クライアントを適切に使用するように導くためにできることはあります (効果的な Java 2nd Edition、項目 58: 回復可能な条件にはチェック済みの例外を使用し、プログラミング エラーの実行時例外と項目 62: 各メソッドによってスローされたすべての例外を文書化します)。クライアントが特定のメソッドの戻り値を無視しないように強制する注釈を持ち、コンパイル時にエラーまたは警告の形でコンパイラによって強制されるようにすることは、この考えに沿っているようです。
付録 2: スニペット
以下は、私が達成したいことを簡潔に説明する予備的な試みです。
上記のコードは正常にコンパイルおよび実行されます ( ideone.com で見られるように)。そうならないようにするにはどうすればいいですか?必要なセマンティクスをどのように割り当てることができ@Undiscardable
ますか?
rest - 長期的なタスクのための REST API を作成する最良の方法は?
2 つのサーバーがあるとします。
1 つ目は、長時間 (数分から数時間) かかる計算を提供するサービスです。
2 番目のサーバーは、このサービスを使用してデータを計算します。
最初のサーバー用の REST API を設計しようとしていますが、これまでのところうまくいっています。しかし、長期にわたるタスクが終了したときに通知をモデル化する方法について、意見を聞きたいと思います。
これまでに2つのアプローチを検討しました。
- ポーリング - 2 番目のサーバーは時々結果を尋ねます。
- コールバック - 2 番目のサーバーは、完了後に最初に呼び出すサーバーの uri をセットアップします。しかし、これは REST API では少し臭いです。
どう思いますか?
windows - Windows API が他の API よりも単純ではないのはなぜですか?
Windows API は、皆さんが期待するほど単純ではないように思えます。私にとって、彼らはやや複雑なファッションですか?
これは後方互換性を保つ効果でしょうか?
Microsoft の主な目標は、開発者を ATL/MFC、VB、および/または .net などのより高いレベルの抽象化にプッシュすることですか?
Win32 API を使用するのは初めてで、このような呼び出しFindFirstFile
により、API は 10% の労力で 90% の柔軟性を達成するように見える UNIX の哲学を固く信じています。fcntl
また、Windows での実際の呼び出しに不可欠な *nix タイプのマシンなどで呼び出しを使用することを保証するいくつかのユニークなケースがあるようです。
... それとも、基本的なパラダイムが欠けているだけですか?
f# - F#のCollections.Seqモジュールが基本的にすべてのEnumerable拡張メソッドを再実装するのはなぜですか?
Collections.Seq
モジュールに、で宣言された拡張メソッドと同等のように見えるメソッドがたくさんあるのはなぜSystem.Linq.Enumerable
ですか?F#の設計者が、.NETにすでに存在するものを再利用するのではなく、これらすべてに新しい名前空間と新しい/異なる名前を作成する必要性を感じたのはなぜですか?
(追加のメソッドが必要な場合、なぜそれらを追加しなかったのSystem.Linq.Enumerable
ですか?)