いつインターフェイスを返し、いつ具象クラスを返すべきでしょうか?
-
06-09-2019 - |
質問
Java でプログラミングするとき、私はほとんどいつも、ただの習慣で次のようなものを書きます。
public List<String> foo() {
return new ArrayList<String>();
}
ほとんどの場合、何も考えずに。さて、質問は次のとおりです。したほうがいい いつも 戻り値の型としてインターフェイスを指定しますか?それとも、インターフェイスの実際の実装を使用することをお勧めしますか? 使用する場合、どのような状況で使用しますか?
このインターフェイスを使用すると多くの利点があることは明らかです (それがインターフェイスが存在する理由です)。ほとんどの場合、ライブラリ関数でどのような具体的な実装が使用されるかはあまり重要ではありません。しかし、それが重要な場合もあるかもしれません。たとえば、リスト内のデータに主にランダムにアクセスすることがわかっている場合、 LinkedList
悪いでしょう。しかし、ライブラリ関数がインターフェイスのみを返す場合は、まったくわかりません。安全を期すために、リストを明示的にコピーする必要があるかもしれません。 ArrayList
:
List bar = foo();
List myList = bar instanceof LinkedList ? new ArrayList(bar) : bar;
でもそれはひどいことだと思うし、おそらく同僚がカフェテリアで私をリンチするだろう。そして当然のことながらそうです。
皆さんはどう思いますか?あなたのガイドラインは何ですか?いつ抽象的な解決策に向かう傾向がありますか?また、潜在的なパフォーマンス向上のために実装の詳細をいつ明らかにしますか?
解決
たとえば、私は私がすることがわかっている場合 主に、リスト内のデータにアクセスします ランダムに、LinkedListのは悪いだろう。 しかし、私のライブラリ関数の場合のみ インターフェースを返し、私は単純にしないでください 知っています。安全のために、私は可能性があります でも、明示的にリストをコピーする必要があります ArrayListにオーバーます。
他のみんなが述べたように、あなただけのライブラリはカップリングや図書館の増加保守性を減らすために、機能を実装しているかを気にしてはなりません。
あなたは、ライブラリー・クライアントとして、実装はあなたのユースケースのためにひどく実行している、あなたはその後、担当者に連絡して、(この場合のための新しい方法に従うか、単に変更するための最良のパスについて話し合うことができることを実証することができた場合実装)。
言った、あなたの例では、時期尚早の最適化のにおいます。
方法があるか、重要なことができた場合は、、それはマニュアルに実装の詳細を言及することがあります。
他のヒント
実装の詳細を隠蔽するための適切なインターフェイスを返します。あなたのクライアントは、あなたがそれを実装していないか、何についてのあなたのオブジェクトのオファーを気にする必要があります。あなたが(例えば、LinkedLiskなど、リストをスキップ)民間のArrayListで始まり、および他のその何かに後で決める場合は、インターフェイスを返す場合は、クライアントに影響を与えずに実装を変更することができ、より適切です。瞬間あなたは機会が失われている具体的な型を返します。
OO プログラミングでは、データをできるだけカプセル化したいと考えます。実際の実装を可能な限り隠し、型を可能な限り抽象化します。
この文脈で私が答えるなら、 意味のあるものだけを返す. 。戻り値が具体的なクラスであることはまったく意味がありますか?あなたの例では、次のように自問してください。foo の戻り値に対して LinkedList 固有のメソッドを使用する人はいるでしょうか?
- 「いいえ」の場合は、上位レベルのインターフェイスを使用してください。はるかに柔軟で、バックエンドを変更できます。
- 「はい」の場合は、次のように自問してください。コードをリファクタリングして上位レベルのインターフェイスを返すことはできませんか?:)
コードが抽象的であればあるほど、バックエンドを変更するときに必要な変更は少なくなります。それはとても簡単です。
一方、戻り値を具象クラスにキャストすることになった場合は、おそらく具象クラスを返す必要があるという強い兆候です。ユーザーやチームメイトは、多かれ少なかれ暗黙の契約について知る必要はありません。具象メソッドを使用する必要がある場合は、わかりやすくするために具象クラスを返すだけです。
一言で言えば:コード 抽象的な, 、 しかし 明示的に :)
CSの引用符(私は独学だ)の連でそれを正当化することができることがなければ、私はいつも「派生以上を受け入れ、ほとんどの派生を返す」のクラスを設計するとき、それが立ったのマントラで行ってきました私も長年にわたって。
私は、それが具体的なリターン対インターフェースの面で意味を推測しますが、依存性を低減し、および/または分離しようとしている場合は、インタフェースを返すことは、一般的に、より有用であることです。ただし、具体的なクラスが実装よりはそのインターフェイスよりも、それは通常、バック具象クラスを得るためにあなたの方法の呼び出し元に、より便利である(すなわち、「最も派生」)aribtrarilyにそれらを制限するのではなく、その返されたオブジェクトの機能のサブセット - あなたが実際にをしない限り、のそれらを制限する必要があります。その後、再び、あなたはまた、単にインターフェイスのカバレッジを向上させることができます。私はクラスの軽率シールと比較し、このような不必要な制限。あなたは、決して知らない。ただ、(他の読者のために)そのマントラのかつての一部について少し話をする、少なくともを受け入れることも、あなたの方法の発信者のために最大限の柔軟性を与える派生ます。
-Oisin
一般に、このようなAPIのような公共の対向インターフェイスのため、(例えばList
など)具体的な実装上(例えばArrayList
など)インタフェースを返すこと良いだろう。
ArrayList
またはLinkedList
の使用は、そのライブラリの最も一般的なユースケースのために考慮すべきライブラリの実装の詳細です。それは、処理が簡単になるだろう施設を提供している場合そしてもちろん、内部的に、private
sをハンドオフLinkedList
メソッドを持つことは、必ずしも、悪いことではないでしょう。
他のいくつかのList
クラスは、後に使用されるだろうと信じる十分な理由がない限り、具体的なクラスは、実装で使用すべきではないという理由はありません。しかし、その後、再び、実装の詳細を変更する限り、公共の対向部分がうまく設計されているほど痛みを伴うことはないはずです。
ライブラリ自体は、その消費者にブラックボックスである必要があります。それはまた、それが意図された方法で使用するように設計されるようにライブラリーを設計する必要があることを意味します。
原則として、私はそうであってもわずかしか、私は、ライブラリのいくつかのプライベート、内部の仕組みにいる場合は、内部実装をバックパス、および。公共と私はインターフェイスを使用し、私のモジュールの外部から呼び出される可能性があり、また、Factoryパターンすべてのために。
このようにインターフェイスを使用すると、再利用可能なコードを記述するための非常に信頼性の高い方法であることが証明された。
主な質問が既に回答されている、あなたは、常にインターフェイスを使用する必要があります。私はしかし、ちょうどにコメントしたいと思います。
O(n)と、典型的には、データのLOT - - あなたは、あなたが悪いのランダムアクセス性能を持って知っているデータ構造を返却する場合、インターフェイスを使用して(それがあります理由です)多くの利点を持っていることは明らかです。ほとんどの場合、それは本当に問題で具体的なものを実装ライブラリ関数によって使用されていません。しかし、おそらくそれは問題ではない場合があります。私は主にランダムリスト内のデータにアクセスすることがわかっている場合たとえば、LinkedListのは悪いだろう。私のライブラリ関数が唯一のインターフェイスを返す場合でも、私は単に知りません。安全のために私もArrayListにかけて、明示的にリストをコピーする必要があるかもしれません。
他のインターフェイス、誰もが使用しているので、あなたが反復処理可能なように、リストの代わりに指定する必要がありますがありますライブラリは、唯一のシーケンシャルアクセスが利用可能であることを十分に認識されます。
戻るには右のタイプを選ぶことがちょうど具体的な実装に対してインタフェースに関するものではありません、それが正しいインターフェイスを選択することでもある。
これは、APIメソッドは、インターフェースや具体的なクラスを返すかどうかではない問題ですべてのことくらい。ここでは誰もが言っているにもかかわらず、コードが書かれたら、あなたはほとんどimplementiationクラスを変更することはありません。
これまで以上に重要な情報:常にのパラメータのあなたの方法のための最低限のスコープインタフェースを使用!そうすれば、クライアントは最大の自由を持っているし、あなたのコードがさえ知らないクラスを使用することができます。
APIメソッドがArrayList
を返すと、私はそれと全く良心の呵責を持っていないが、それはArrayList
(または、すべての一般的に、Vector
)パラメータを要求する場合、それはそのIを意味するので、私は、プログラマを追い詰めると、彼を傷つけ考えますArrays.asList()
、Collections.singletonList()
またはCollections.EMPTY_LIST
を使用することはできません。
異論があって申し訳ありませんが、基本的なルールは次のとおりだと思います。
- のために 入力 引数が最も多く使用する ジェネリック.
- のために 出力 価値観、最も 特定の.
したがって、この場合は実装を次のように宣言します。
public ArrayList<String> foo() {
return new ArrayList<String>();
}
理論的根拠:入力ケースはすでに知られており、誰もが説明しています。インターフェイスを使用します。ただし、出力ケースは直観に反して見える場合があります。クライアントに受信内容に関する最大限の情報を提供したいため、実装を返す必要があります。この場合、 より多くの知識はより多くの力をもたらす.
例 1:クライアントは 5 番目の要素を取得したいと考えています。
- コレクションを返す:リストを返すのではなく、5 番目の要素まで反復する必要があります。
- リストを返す:
list.get(4)
例 2:クライアントは 5 番目の要素を削除したいと考えています。
- リストを返す:指定された要素なしで新しいリストを作成する必要があります (
list.remove()
はオプションです)。 - ArrayList を返す:
arrayList.remove(4)
したがって、インターフェイスを使用することは、再利用性を促進し、結合を減らし、保守性を向上させ、人々を幸せにするため、素晴らしいことであるというのは大きな真実です...ただし、次のように使用される場合のみ 入力.
したがって、繰り返しになりますが、ルールは次のように言えます。
- 提供するものに対して柔軟になってください。
- 提供する内容について有益な情報を提供してください。
ということで、次回は実装を返してください。
あなたは離れて実際の実装から抽象へのインタフェースを使用しています。インターフェイスは、基本的に、あなたの実装が何ができるかのためだけの青写真である。
彼らはあなたがその消費者のいずれかが直接影響を受けていることを恐れることなく、実装の詳細を変更することができるため、インターフェースは、限り、あなたはまだない実装として、あなたのインターフェースはそれがない言う、良いデザインです。
あなたは、このようにそれらをインスタンス化しますインターフェースを操作するには:
IParser parser = new Parser();
今IParserは、あなたのインターフェースとなり、そしてパーサーは、実装になります。あなたは上からパーサーオブジェクトを操作する場合さて、あなたは、順番に、あなたの実装(パーサ)に対して動作するインターフェイス(IParser)に対して動作します。
それはそれはあなたのIParserパーサインターフェイスに対して動作するコードに影響を与えることはありません、あなたが好きなだけパーサの内部の仕組みを変えることができることを意味します。
一般的にはすべてのケースでのインターフェイスを使用しています。リストのために、Javaはランダム・マーカークラスは、主に、アルゴリズムは、get(i)は、一定の時間であるかどうかを知っておく必要があるかもしれない一般的なケースを区別します。
は、コードの使用のために、マイケルは、上記の方法のパラメータで可能な限り汎用的であることは、多くの場合、さらに重要であることを右です。このような方法をテストする場合には特にそうです。
あなたがインターフェイスを返すように、彼らはあなたのコードを透過することがわかります(または発見しました)。例えばその後、方法Bにインターフェースを渡すために、あなたは、メソッドAからインターフェイスを返すと、あなたはのHAVE を。
あなたがやっていることは限られた形ではあるが、契約によってプログラミングです。
このはあなたに(これらの新しいオブジェクトは、既存の契約/期待される行動を満たす提供)カバーの下に実装を変更するには、巨大な範囲を提供します。
( - 例えば、からかっ使用してテストを含む)このすべてを考えると、あなたはあなたの実装を選択するという点でメリットがあり、どのように行動を置き換えることができます。場合あなたは、私はこのすべてに賛成だ、推測とに減らす(または紹介)しようとしていなかったのインターフェース可能な限ります。