博覧会:
私はこの些細なインタフェースを持っていると仮定します。
interface Y { Y f(); }
私は3種類の方法でそれを実装することができます:
どこでも一般的なタイプを使用してください。
class SubY_generalist implements Y { public Y f() { Y y = new SubY_generalist(); ... return y; } }
特殊なタイプを使用しますが、暗黙のうちに一般的な型にキャストし、同じ値を返します。
class SubY_mix implements Y { public Y f() { SubY_mix y = new SubY_mix(); ... return y; } }
特殊なタイプを使用し、それを同じに戻します。
class SubY_specialist implements Y { public SubY_specialist f() { SubY_specialist y = new SubY_specialist(); ... return y; } }
私の考慮事項:
あり長い会話が近く、ここでのメリットに「インターフェイスにプログラミング」。ほとんどの昇格の答えは、実際にある引数と戻りの型、の区別に深く入るようには見えません根本的に異なります。議論は別の場所できれいなカットの答えを私に提供していないことが発見、私は選択肢がないが、自分でそれを推測する-種類読者はもちろん、私に手を貸すことができない限り。
私は、Javaについては、次の基本的な事実を前提とします:(彼らは正しいですか?)
- オブジェクトは、その最も特別に作成されます。
- これは、暗黙のうちに、より一般的な型にキャストすることができる(一般)いつでも。
- オブジェクトが一般化されている場合、それはその有用な特性の一部を失ったが、どれを獲得しません。
これらの単純な点から、特別なオブジェクトがより有用でなく、一般よりも危険であるということになります。
一例として、私が不変であることに一般化することができる可変容器を有していてもよいです。私は、コンテナは、このように、凍結される前に、いくつかの有用な性質を持っていることを確認した場合、私は誤って不変を壊すからユーザーを防ぐために、適切なタイミングでそれを一般化することができます。しかし、これは正しい方法ですか?同様の分離を達成するための別の方法があります:私は常にちょうど私の方法を作るかもしれパッケージプライベート。一般化は、より柔軟であるように思わなく、簡単omissと微妙なバグのために表面を導入します。
しかし、いくつかの言語で、Pythonのように、それは、外部からのアクセスから、これまで実際に保護する方法に不必要であると考えられます。彼らは、アンダースコアと内部メソッドにラベルを付けます。熟練したユーザーは、彼らがすべての複雑さを知っていることを提供し、いくつかの利得を達成するために内部メソッドにアクセスすることができます。
他の結果は、メソッドの定義内で、私は専門的なオブジェクトを好むべきであるということです。
私の質問:
- これは正しい考え方ですか?
- 私何か不足していますか?
- これはどのようにの話に関連しないインターフェイスにプログラミング?ここではいくつかの地元の人々は、それが適切であると考えているようだ、と私はそれを見るように私は、だけではないすぐに、それがないことに同意します。これは、より多くのようなものですインターフェイスに対してプログラミングここでは、またはサブクラスに対する一般的なインチ 私は、これらの複雑さに関する損失のビットをしています。
ここで私は、トピックについて話を使用している状況です。関数fは、以下のオプションもありましょう:
interface Y { ... }
class SubY implements Y { ... }
DEFINITION CHOICE:
public SubY f() { ... }
OR
public Y f() { ... }
USAGE CHOICE:
...
Y y = f();
OR
SubY y = f(); //maybe with a cast
...
技術的には、すべてのオプションは、あなたがの詳細公開するつもりかどうかに応じて、正しいことができSubY
、エンドユーザー(次のプログラマ)にかどうかを。それが公開する悪い考えのように見える場合はSubY
、エンドユーザーには、しないでください。そうでなければ、行います。ここでは推論です:
あなたは、常に(狭いタイプはさらに下ある概念的に、必要になることができ、最も狭いタイプを返す必要がありますクラス階層 - 「狭いタイプが広いタイプである」として扱います)。たとえば、あなたが戻るならばList
手段を使用すると、エンドユーザーが唯一のインターフェースの面で、それとの対話を期待していることを、。ただし、エンドユーザーが内に定義された相互作用が必要になることが予想される場合ArrayList
、代わりにそれを返します。
一般的に、アイデアは、エンドユーザが知っておくべきではない血みどろの詳細を露出することなく、可能な限り狭いような方法からの戻り型を維持することです。あなたが適切にあなたのクラスによって処理内部の詳細を隠す限りSubY
、それは完全にある細かい戻りますSubY
。一方、エンドユーザーの責任を考慮してください。
エンドユーザーのresponsibiltyは責任を持って、彼らはあなたの狭い戻り値の型によって与えられてきた電源を使用することです。あなたは既に適切に内部を非表示にすることで、嫌なことをやってからそれらを妨げてきました。エンドユーザーが自分のクラスを使用する場合その後、彼らはそうのように、彼らのニーズの面でプログラムしなければなりません。
// imported library provides:
public SubY f() { ... }
... // la la la
Y y = f();
useYFunctionality(y);
... // somewhere else that you need SubY functionality
SubY subY = f();
useSubYFunctionality(subY);
したがって、プログラマは(それが安全である最も狭い可能な実装を返し、正確な型を返すようにその機能を定義する必要ができるインターフェイスです)。一方、そのサービス/機能のエンドユーザーは、(あれば意思の結合と増加明確さを低下させるために、まだ動作可能な限り広いタイプとしてその変数を定義する必要がありgetLicensePlateNumber
ますが、使用に必要なすべてであるVehicle
代わりにVeryUnreliableCar
)。ここで重要なのは、戻り値の型を選択すると、持っていないということである明確な答えを。代わりに、エンドユーザーに公開してはならない必要があるかを決定するためにあなたの判断を使用しています。アクセスを否定する例外的な理由はありません場合はSubY
、その後、心の中で、この原則を守る:Functions Return Accurately, Users Define Variables As Necessary
、またはFRAUDVAN。
あなたの例にこれを適用します:
class SubY_generalist implements Y
{
public Y f()
{
Y y = new SubY_generalist();
...
return y;
}
}
- どこにでもインタフェースを使用。あなたが唯一のインタフェースの詳細を気にするときこれが最善である、と同じことが、エンドユーザーのためにも当てはまります。たとえば、あなただけを心配している場合
get
やadd
、Y
可能性List
、そしてSubY_generalist()
可能性がありArrayList
。
class SubY_mix implements Y
{
public Y f()
{
SubY_mix y = new SubY_mix();
...
return y;
}
}
- 特定のタイプを使用しますが、暗黙のうちに復帰時の一般的な型に広がります。あなたが実際に特定のタイプのための方法を使用する必要があるとき、これは最高ですが、あなたのエンドユーザーがします決してそうする必要はありません。使用
ArrayList
およびList
前述の例から、と仮定するf
マスト呼び出しtrimToSize
、で定義されていない方法List
。その後、それが宣言することが明らかに必要だy
としてArrayList
。しかし、エンドユーザーにのみ必要があるのでList
、あなたは返す必要がありますy
し、暗黙的に広げています。
class SubY_specialist implements Y
{
public SubY_specialist f()
{
SubY_specialist y = new SubY_specialist();
...
return y;
}
}
- 専門的なタイプを使用し、専門的な型を返します。これはFRAUDVANの「本質」です。
SubY_specialist
戻り型としてユーザに対処する柔軟性を可能にSubY_specialist
返さ、または代替的に使用するf()
インタフェースの観点からしますY
。これは非常に共通のインタフェースが不十分(彼らはしているので、彼らは、あるべきとして効果的に使用される特定されたときに、抽象的)。たとえば、JavaでStream
クラス、メソッドが返す任意の時間はStream
、これが起こっています。これは、あるStream
実装BaseStream
。それはまた、あなたが取るすべての時間に発生StringBuffer
してreverse
、append
、replace
、delete
、またはinsert
(StringBuffer
実装CharSequence
)。
このような動作が必要な理由は、インタフェースは、多くの場合、エンドユーザーによって有効に利用されるのに十分な詳細が含まれていないということです。インターフェイスでこれらの詳細を置くことは望ましくない界面の膨張/汚染をもたらすであろう。代わりに、直接、それらの詳細を担当するクラスは、ユニークな機能を持っているクラスのためにジャムに単一責任の原則を使用すること持つ(回避はインタフェースの将来の実装で冗長性を実装することを余儀なくされています)。結局のところ、あなたのクラスが実装二つのインタフェース場合は、いずれかのこれらのインターフェースと(かなり迅速に醜い得ることができる)というリターンの両方を拡張する新しいインターフェイスを作成する必要があります。または、実装を返し、エンドユーザがを担当することができますそのインターフェイス、彼らは(醜いではない)が必要です。
なぜ一般的なので、このような動作はありますか?あなたは、エンドユーザーが(可視性改質剤、カプセル化、および情報隠蔽を使用)を参照してくださいする必要がないという事を離れて非表示にしたらそれが実際だだ、ので、良いアイデア、彼らが選択したこと方法であなたの実装と対話するエンドユーザーをできるように(すなわち、それらはとしての変数を宣言するインターフェイスごとなど)。実は、私もあり、ここですべての例は、「最も安全な」タイプは、ユーザーに公開するFRAUDVANに準拠していることを主張したいY
の代わりに特殊なタイプの。最初の例では、インタフェースがY
十分にユーザとエンドユーザに特化されています。第2の例では、Y
不十分なユーザに特化されているが、十分にエンドユーザに特化。最後に、この例では、Y
一部のエンドユーザーが宣言するために選ぶかもしれないが不十分、ユーザーおよびすべてのエンドユーザーに特化されたY y = f()
場合はY
自分のニーズに合うだろう。
だから、ここ持ち帰りです: