18

抽象クラスとは対照的に、インターフェイスをいつ使用するか、またはその逆をいつ使用するかを理解するのに問題があります。また、インターフェイスを別のインターフェイスに拡張するタイミングがわかりません。長い投稿については申し訳ありませんが、これは非常に紛らわしいです。

図形を作成することは、人気のある出発点のようです。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の関係を壊します。だから私は混乱しています...

4

7 に答える 7

6

抽象クラスまたはインターフェースを使用するときは、基本的な概念を覚えておいてください。

抽象クラスは、拡張するクラスがそれを実装するクラスにより密接に結合されている場合、つまり両方が親子関係にある場合に使用されます。

例:

       abstract class Dog {}

       class Breed1 extends Dog {}

       class Breed2 extends Dog {}

Breed1どちらも犬のタイプでありBreed2、犬としての共通の行動があります。

一方、実装クラスにクラスから実装される機能がある場合、インターフェイスが使用されます。

     interface Animal {
         void eat();
         void noise();
     }

     class Tiger implements Animal {}

     class Dog  implements Animal {}

TigerDogは 2 つの異なるカテゴリですが、どちらも食べることと音を立てることは異なります。そのため、 から食べるとノイズを使用できますAnimal

于 2012-11-16T07:36:53.553 に答える
3

1 つ以上のメソッドを抽象化しない場合は、抽象クラスを使用します。

すべてを抽象化したい場合は、インターフェースを使用してください。

于 2012-07-09T05:14:10.927 に答える
3

これは、通常より少し複雑なクラス階層を設計するときに出てくる質問です。しかし、一般に、抽象クラスとインターフェースを使用する際に知っておく必要があることはほとんどありません。

抽象クラス

  • コンストラクターとコンストラクターのオーバーライドを使用する機能を活用できます
  • 多重継承を持つクラスを制限する (これは、複雑な API を設計している場合に特に役立ちます)
  • インスタンス変数とメソッドの実装
  • メソッド super 呼び出しの機能を活用します (親抽象クラスの実装を呼び出すために super を使用します)

インターフェース

  • 多重継承を有効にします - n 個のインターフェースを実装できます
  • 概念的なメソッドのみを表すことができます (メソッド本体はありません)

通常、「-able」句にはインターフェイスを使用します(機能のように)。例えば:-

  1. Runnable
  2. Observable

is-a(evolution format) のようなものには抽象クラスを使用します。例えば:-

  1. Number
  2. Graphics

しかし、厳格なルールを作成するのは簡単ではありません。お役に立てれば

于 2012-07-09T06:05:32.103 に答える
1

ここでかなりの数の質問があります。しかし、基本的には、インターフェースと抽象クラスについて質問していると思います。

インターフェイスを使用すると、複数のインターフェイスを実装するクラスを持つことができます。ただし、API として使用する場合、インターフェースは永続的ではありません。インターフェイスが公開されると、他の人のコードを壊してしまうため、インターフェイスを変更するのは困難です。

抽象クラスでは、1 つのクラスのみを拡張できます。ただし、他の人のコードを壊すことなく後のバージョンで変更できるため、抽象クラスは API に対して永続的です。また、抽象クラスを使用すると、事前定義された実装を持つことができます。たとえば、三角形の例では、抽象クラスの場合、デフォルトで 3 を返すメソッド countEdges() がある場合があります。

于 2012-07-09T05:25:34.797 に答える
1

これは非常に頻繁に出てくる質問ですが、誰もが満足する唯一の「正解」はありません。

クラスはis-a関係を表し、インターフェースはcan-do動作を表します。私は通常、いくつかの経験則に従います。

  • インターフェイスが必要であることが確実でない限り、クラス (抽象/具象) に固執します。
  • インターフェイスを使用する場合は、非常に具体的な機能に分割してください。インターフェイスにいくつかのメソッドが含まれている場合は、間違っています。

さらに、形や人物 (またはそのことについては吸血鬼!) のほとんどの例は、通常、現実世界のモデルの貧弱な例です。「正しい」答えは、アプリケーションが必要とするものによって異なります。たとえば、次のように言及しました。

class Vampire extends Monster implements Dangerous, Lethal, BloodSuckable

あなたのアプリケーションはこれらすべてのインターフェースを本当に必要としていますか? s にはいくつの種類Monsterがありますか? 実際にそれ以外のクラスVampireを実装していBloodSuckableますか?

一般化しすぎないようにして、不要なときにインターフェイスを抽出してください。これは経験則に戻ります。ユースケースでインターフェースが必要な場合を除き、単純なクラスを使用してください。

于 2012-07-09T05:35:09.673 に答える
0

あなたの形の例は良いです。私はそれを次のように見ています。

共有されているメソッドまたはメンバー変数がある場合、抽象クラスしかありません。あなたの例でShapeは、実装されていないメソッドが 1 つしかありません。その場合は、常にインターフェイスを使用してください。

Animalクラスがあったとしましょう。各動物は、手足の数を追跡します。

public abstract class Animal
{
    private int limbs;
    public Animal(int limbs)
    {
        this.limbs = limbs;
    }

    public int getLimbCount()
    {
        return this.limbs;
    }

    public abstract String makeNoise();
}

各動物の手足の数を追跡する必要があるため、スーパークラスにメンバー変数を含めることは理にかなっています。しかし、それぞれの動物は異なる種類の音を立てます。

したがって、メンバー変数と実装されたメソッドと抽象メソッドがあるため、それを抽象クラスにする必要があります。

2 番目の質問では、これを自問する必要があります。

三角形は常に形状になりますか?

その場合、三角形を Shape インターフェイスから拡張する必要があります。

結論として、最初の一連のコード例でインターフェイスを選択します。最後のセットで、2 番目の方法を選択します。

于 2012-07-09T05:14:46.000 に答える