私が2つのクラスを持っていると仮定します(単純化するためのメソッドを示していません):
public interface Fruit{
}
public class Orange implements Fruit{
}
、そして私が実際の違いなしにOrangeを初期化する2つの方法を使用できると仮定します(私のコードでは両方とも同じようにコンパイルして動作することができることを意味します):
1.Abstract one:
Fruit obj=new Orange();
2.具体的なもの:
Orange obj=new Orange();
「インターフェイスへのプログラミング」を理解する に従って、私は理解しているように、抽象的な「インターフェイスへのプログラミング」に適合し、カップリングが少ないため、抽象的なものを使用する必要があります。
しかし、抽象的な方が2つのクラスのキーワードを持っていると思うので、抽象的な方がカップリングが少ない理由がよくわかりません。抽象的な方は、フルーツとオレンジの2つのキーワードを持っています。クラス。抽象化により、オレンジから他のタイプ(例:グレープ)に切り替えて、文字の消去と入力を減らすことができる可能性があることに同意しますが、切り替え後も2つのキーワード(フルーツとグレープ)が含まれます。
また、抽象的なものの他の欠点は、ある日、Orangeがインターフェイスを削除するか、他のインターフェイスに変更する必要があることです。
public class Orange{
}
または公開インターフェースCircleShape {}
public class Orange implements CircleShape{
}
、抽象的なものは変更する必要がありますが、具体的なものは変更する必要はありません。したがって、具体的な方が抽象的な方よりも結合が少ないと思います。本当?そうでない場合、ここでの誤解は何ですか?
ここでの結合は、Orange
とプログラムの残りの部分との間で発生します。 Orange
のインスタンスを保持している場合、Orange
ではなくFruit
に固有のメソッドを呼び出すことができます。これは2つの理由で良くありません:A)Orange
に固有のメソッドを使用すると、そのインスタンスをFruit
インスタンスとして単に操作できなかった(Liskov置換の原則に違反する)ことと、B)それが意味すること「オレンジ」は「果物」ではなく、動作が異なります。
より具体的な例を使用して、新しいリストが必要であるとします。
ArrayList<String> list = new ArrayList<String>();
これをメソッドに渡したいと判断すると、当然、メソッドは次のようになります。
public void handleList(ArrayList<String> list) {
// ...
}
考えてみれば、handleList
メソッドは、それがArrayList
またはList
のインスタンスである可能性が高いことを特に気にしません。そのコレクション内のすべてのオブジェクトを少なくとも1回処理するだけで済みます。したがって、次のように安全に書き換えることができ、他の実装(独自のインクルードが含まれている可能性があります)を渡すことができます。
public void handleList(Collection<String> list) {
// ...
}
これをさらに進めたい場合は、Stringの代わりにワイルドカード(?)を使用し、コレクション内のすべてのアイテムでtoString()を呼び出します。ポイントは、ArrayList
のインスタンスを使用する義務がないことです。代わりにLinkedList
またはHashSet
またはTreeSet
を使用できます。
例に戻ると、理想的にはプログラムで、Orange
またはGrape
クラスをoneに正確に配置するのではなく、Apple
を使用するという事実に注意する必要があります。プログラムの中にを置きます。この後は、これらのインスタンスをFruit
としてのみ使用するか、別の言い方をすれば、実装方法は気にせず、used。
つまり、実装間で共通の機能を持つ抽象共通クラスを用意すると便利です。これはnotインターフェースの代わりです。インターフェイスは、それがusedになる方法を定義します。抽象クラスは、実装を削減および簡素化するためのものです。おそらく、複数の抽象クラスがあり、それぞれにいくつかの具体的な実装があります。これらはすべて、プログラム全体で使用する単一のインターフェースから派生しています。
詳細については、 この記事 を参照して、リスコフの置換原理、特にオープンクローズの原理に関する部分について少し説明してください。プログラムでオブジェクトを定義する場所とオブジェクトを使用する場所を明確に理解する必要があります。それらが同じ場所にない場合は、最も確実に、インターフェースを使用してusedの使用方法を定義する必要があります。
一般原則として、必要最小限の情報を使用する必要があります。
あなたの例では、OrangeがFruitインターフェイスを実装していて、オブジェクトがOrangeオブジェクトであるという知識がなくてもコードが正常に機能する場合、Fruitインターフェイスとして宣言する必要があります。つまり、Strawberryオブジェクトが必要な場合は、最初の行を "Fruit obj = new Strawberry();"に変更する以外は何も変更する必要はありません。もちろん、次のコードの一部がOrangeオブジェクトを想定し、Fruitインターフェースに存在しないメソッドを使用する場合は、「Orange obj = new Orange();」から始めます。そして、あなたのコードはStrawberryオブジェクトで動作するだけではないことを知っています。それは難しいです。コードの変更が必要ですが、これらのコードの変更が必要です。
Orangeが実装しているインターフェースを変更した場合、そのインターフェースを変更することはできません。両方のインターフェースを実装します。または、Fruitに必要なメソッドを削除し、新しいインターフェイスのメソッドを追加します。 Orangeオブジェクトを作成するコードは機能しなくなります。 Orange obj = new Orange();を宣言するおそらくあなたはもう存在しないインターフェースメソッドを呼んでいたので、助けにはならなかっただろう。インターフェースの変更はmajor変更であり、大きな影響があります。 FruitSaladクラスは、Fruitインターフェースを実装しない場合、Orangeオブジェクトを処理できない可能性があります。
これは、
なぜとにかく抽象化するのですか?
抽象化の要点は少ない方が多いを行うことです。コードをrulesのように見たとき暗いスーツと黒い帽子をかぶっている男性とは話さないでくださいと見知らぬ人と話さないでください) 2つのルールがあり、後者は前者よりも抽象的です。
その結果、単語が少なくなり、より一般的で、より多くのケースに対処する抽象ルールができあがります。
しかし、医師と話すはどうですか?a)見知らぬ人とb)男性かもしれませんか?
その場合、ルールは意味をなさず、ルールの例外に対処する必要があります。
これはあなたの問題とどう関係していますか?
Fruit
のインターフェースを持つことで、抽象化を行いました。そして、fruits
のように動作する$things
を扱うすべての命令は非常に一般的であり、多くのfruityに適用できます。
知っているすべてのFruit
のコードを記述したくありません。
しかし、上記の例のように、Orange
動作が必要な場合、これはばらばらになります。これはonlyフルーティーな動作よりも豊富な場合があります。
したがって、「インターフェースへの実装」は経験則であり、次のことを意味します。
共通の動作を共有するオブジェクトをインターフェイスと一緒にグループ化し、これらのオブジェクトが個々のオブジェクトよりも少ないコードを書くように命令を書くことは良い考えです。しかし、時々このルールは成り立たない。
PS:「カップリング」とは、この「専門化に依存する」またはあまり一般的ではないを意味します。したがって、一般ルールを作成する場合、特殊化=結合を回避します。