抽象クラスとは対照的に、インターフェイスをいつ使用するか、またはその逆をいつ使用するかを理解するのに問題があります。また、インターフェイスを別のインターフェイスに拡張するタイミングがわかりません。長い投稿については申し訳ありませんが、これは非常に紛らわしいです。
図形を作成することは、人気のある出発点のようです。2D形状をモデル化する方法が必要だとしましょう。それぞれの形に面積があることを私たちは知っています。次の2つの実装の違いは何でしょうか。
インターフェース付き:
public interface Shape {
public double area();
}
public class Square implements Shape{
private int length = 5;
public Square(){...}
public double area()
return length * length;
}
}
抽象クラスの場合:
abstract class Shape {
abstract public double area();
}
public class Square extends Shape {
private length = 5;
public Square(){...}
public double area(){
return length * length;
}
抽象クラスを使用するとインスタンス変数を定義でき、メソッドの実装を提供できるのに対し、インターフェイスではこれらのことを実行できないことを理解しています。ただし、この場合、これら2つの実装は同じように見えます。それで、どれを使っても大丈夫ですか?
しかし、ここで、さまざまなタイプの三角形について説明したいとします。二等辺三角形、鋭角三角形、直角三角形を作成できます。私にとって、この場合はクラス継承を使用するのが理にかなっています。「IS-A」定義の使用:直角三角形「IS-A」三角形。三角形の「IS-A」形状。また、抽象クラスは、すべてのサブクラス内で共通の動作と属性を定義する必要があるため、これは完璧です。
抽象クラス付き
abstract Triangle extends Shape {
private final int sides = 3;
}
class RightTriangle extends Triangle {
private int base = 4;
private int height = 5;
public RightTriangle(){...}
public double area() {
return .5 * base * height
}
}
これは、TriangleとShapeがインターフェイスであるインターフェイスでも実行できます。ただし、クラスの継承(「IS-A」関係を使用してサブクラスとなるものを定義する)とは異なり、インターフェイスの使用方法がわかりません。私は2つの方法を見ます:
最初の方法:
public interface Triangle {
public final int sides = 3;
}
public class RightTriangle implements Triangle, Shape {
private int base = 4;
private int height = 5;
public RightTriangle(){}
public double area(){
return .5 * height * base;
}
}
2番目の方法:
public interface Triangle extends Shape {
public final int sides = 3;
}
public class RightTriangle implements Triangle {
....
public double area(){
return .5 * height * base;
}
}
これらの両方の方法が機能するように私には思えます。しかし、いつ一方を他方よりも優先して使用しますか?また、抽象クラスよりもインターフェイスを使用してさまざまな三角形を表すことには利点がありますか?形状の記述を複雑にしましたが、インターフェースと抽象クラスの使用は同等のようです。
インターフェイスの重要なコンポーネントは、無関係のクラス間で共有できる動作を定義できることです。したがって、Flyableインターフェイスは、BirdだけでなくAirplaneクラスにも存在します。したがって、この場合、インターフェースアプローチが好ましいことは明らかです。
また、別のインターフェイスを拡張する紛らわしいインターフェイスを構築するには、次のようにします。インターフェイスを決定するときに「IS-A」関係を無視する必要があるのはいつですか。この例を見てください:LINK。
「VeryBadVampire」をクラスにし、「Vampire」をインターフェースにする必要があるのはなぜですか?'VeryBadVampire' IS-A'Vampire'なので、私の理解では、'Vampire'はスーパークラス(おそらく抽象クラス)でなければなりません。'Vampire'クラスは、'Lethal'を実装して、その致命的な動作を維持できます。さらに、「ヴァンパイア」は「モンスター」であるため、「モンスター」もクラスである必要があります。「Vampire」クラスは、「Dangerous」と呼ばれるインターフェースを実装して、その危険な動作を維持することもできます。危険であるが致命的ではない「BigRat」と呼ばれる新しいモンスターを作成したい場合は、「Monster」を拡張して「Dangerous」を実装する「BigRat」クラスを作成できます。
上記は、インターフェースとして「Vampire」を使用した場合と同じ出力を達成しませんか(リンクで説明されています)?私が見る唯一の違いは、クラスの継承を使用し、「IS-A」の関係を維持することで、多くの混乱が解消されることです。しかし、これは守られていません。これを行うことの利点は何ですか?
モンスターに吸血鬼の振る舞いを共有させたい場合でも、オブジェクトの表現方法をいつでも再定義できます。「VeryMildVampire」という新しいタイプの吸血鬼モンスターが必要で、「Chupacabra」という吸血鬼のようなモンスターを作成したい場合は、次のように実行できます。
「Vampire」クラスは「Monster」を拡張し、「Dangerous」、「Lethal」、「BloodSuckable」を実装します。
「VeryMildVampire」クラスは「Vampire」クラス
を拡張します。
しかし、これを行うこともできます。
「VeryMildVampire」は「Monster」を拡張し、「Dangerous、Lethal、Vampiric 」を
実装します。「Chupacabra」は「Monster」を拡張し、「Dangerous、Vampiric」を実装します。
ここでの2番目の方法は、「吸血鬼」インターフェースを作成するため、(最初の例のように)吸血鬼の動作を定義する一連のインターフェースを作成するのではなく、関連するモンスターをより簡単に定義できます。しかし、これはIS-Aの関係を壊します。だから私は混乱しています...