104

質問されたので、ここで回答をレビューしてもらいたいと思いました。

Q:インターフェイスを実装するよりも抽象クラスを拡張する方が適切なシナリオはどれですか?

A:テンプレート メソッド デザイン パターンを使用している場合。

私は正しいですか?

質問を明確に述べることができなかったら申し訳ありません。
抽象クラスとインターフェースの基本的な違いを知っています。

1) 特定の操作 (メソッドの実装) に対してすべてのサブクラスで同じ機能を実装し、他のいくつかの操作 (メソッド シグネチャのみ) に対して異なる機能を実装する必要がある場合は、抽象クラスを使用します。

2)インターフェイスの実装に準拠できるように、署名を同じ(および実装が異なる)にする必要がある場合は、インターフェイスを使用します

3) 最大 1 つの抽象クラスを拡張できますが、複数のインターフェースを実装できます。

質問を繰り返します:上記以外に、特に抽象クラスを使用する必要がある他のシナリオはありますか (テンプレート メソッドの設計パターンは概念的にこれのみに基づいていることがわかります)。

インターフェイス vs. 抽象クラス

これら 2 つのどちらを選択するかは、実際に何をしたいかによって異なりますが、幸運なことに、Erich Gamma が少し助けてくれます。

常にトレードオフがあり、インターフェースは基本クラスに関して自由を提供し、抽象クラスは後で新しいメソッドを追加する自由を提供します。– エーリッヒ・ガンマ

コード内の他の多くのものを変更せずにインターフェイスを変更することはできません。したがって、これを回避する唯一の方法は、まったく新しいインターフェイスを作成することですが、これは必ずしも良いことではありません。

Abstract classes主に、密接に関連するオブジェクトに使用する必要があります。Interfaces無関係なクラスに共通の機能を提供することに優れています。

4

15 に答える 15

92

インターフェイスをいつ使用するか

インターフェイスを使用すると、誰かが最初からインターフェイスを実装したり、元の目的または主な目的がインターフェイスとはまったく異なる他のコードでインターフェイスを実装したりできます。彼らにとって、あなたのインターフェースは付随的なものであり、パッケージを使用できるようにするためにコードに追加する必要があるものです. 欠点は、インターフェース内のすべてのメソッドが public でなければならないことです。すべてを公開したくない場合があります。

抽象クラスを使用する場合

対照的に、抽象クラスはより多くの構造を提供します。通常、いくつかのデフォルトの実装を定義し、完全な実装に役立ついくつかのツールを提供します。問題は、それを使用するコードは、クラスをベースとして使用する必要があることです。あなたのパッケージを使用したい他のプログラマーがすでに独自のクラス階層を独自に開発している場合、これは非常に不便です。Java では、クラスは 1 つの基本クラスからのみ継承できます。

両方を使用する場合

インターフェイスと抽象クラスの両方の長所を提供できます。実装者は、必要に応じて抽象クラスを無視できます。これを行うことの唯一の欠点は、インターフェイス名を介してメソッドを呼び出すことであり、抽象クラス名を介してメソッドを呼び出すよりもわずかに遅くなります。

于 2012-04-06T06:47:11.323 に答える
32

質問を繰り返します: 上記以外に、特に抽象クラスを使用する必要がある他のシナリオがあります (1 つは、テンプレート メソッドの設計パターンが概念的にこれのみに基づいていることです)。

はい、JAXB を使用する場合。インターフェイスが好きではありません。抽象クラスを使用するか、ジェネリックでこの制限を回避する必要があります。

個人のブログ投稿から:

インターフェース:

  1. クラスは複数のインターフェースを実装できます
  2. インターフェイスはコードをまったく提供できません
  3. インターフェイスは public static final 定数のみを定義できます
  4. インターフェイスはインスタンス変数を定義できません
  5. 新しいメソッドを追加すると、クラスの実装に波及効果があります (設計の保守)
  6. JAXB はインターフェースを扱えない
  7. インターフェイスは抽象クラスを拡張または実装できません
  8. すべてのインターフェイス メソッドはパブリックです

一般に、インターフェイスは契約を定義するために使用する必要があります (達成方法ではなく、何を達成するか)。

抽象クラス:

  1. クラスは、最大で 1 つの抽象クラスを拡張できます
  2. 抽象クラスにはコードを含めることができます
  3. 抽象クラスは、静的定数とインスタンス定数の両方を定義できます (最終)
  4. 抽象クラスはインスタンス変数を定義できます
  5. 既存の抽象クラスのコードを修正すると、クラスの拡張に波及効果がある(実装のメンテナンス)
  6. 抽象クラスに新しいメソッドを追加しても、クラスの拡張に波及効果はありません
  7. 抽象クラスはインターフェースを実装できます
  8. 抽象クラスは、プライベートおよび保護されたメソッドを実装できます

(部分的な) 実装には、抽象クラスを使用する必要があります。それらは、API コントラクトの実装方法を制限する手段になる可能性があります。

于 2012-08-11T13:15:15.383 に答える
18

インターフェイスは、すべてのクラスが同じ構造を持ち、機能がまったく異なるというシナリオがある場合に使用されます。

抽象クラスは、すべてのクラスが同じ構造を持っているが、いくつかの同じ機能といくつかの異なる機能を持っているシナリオがある場合に使用されます。

記事をご覧ください: http://shoaibmk.blogspot.com/2011/09/abstract-class-is-class-which-cannot-be.html

于 2012-04-06T06:47:03.273 に答える
15

ここには素晴らしい答えがたくさんありますが、インターフェースと抽象クラスの両方を使用することが最善の方法であることがよくあります。 この不自然な例を考えてみましょう:

あなたは投資銀行のソフトウェア開発者で、市場に注文を出すシステムを構築する必要があります。あなたのインターフェースは、取引システムが何をするかについての最も一般的な考え方を捉えています。

1) Trading system places orders
2) Trading system receives acknowledgements

インターフェイスでキャプチャできますITradeSystem

public interface ITradeSystem{

     public void placeOrder(IOrder order);
     public void ackOrder(IOrder order);

}

これで、セールス デスクや他のビジネス ラインで働くエンジニアが、システムとのインターフェイスを開始して、既存のアプリに発注機能を追加できるようになりました。そして、まだ構築を開始していません。これがインターフェースの力です。

そこで、株式トレーダー向けのシステムを構築します。彼らはあなたのシステムに安い株を見つける機能があると聞いて、それを試してみたいと思っています! と呼ばれるメソッドでこの動作をキャプチャしますfindGoodDeals()が、市場への接続には多くの厄介なものがあることにも気付きます。たとえば、 を開く必要がありますSocketChannel

public class StockTradeSystem implements ITradeSystem{    

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

具体的な実装には、 のような厄介なメソッドがたくさんありますが、トレーダーが実際に気にかけているのはconnectToMarket()それだけです。findGoodDeals()

ここで、抽象クラスの出番です。 あなたの上司は、通貨トレーダーもあなたのシステムを使用したいと言っています。そして通貨市場を見ると、配管は株式市場とほぼ同じであることがわかります。実際、connectToMarket()外国為替市場に接続するために逐語的に再利用できます。ただし、findGoodDeals()通貨の分野ではまったく異なる概念です。したがって、コードベースを海の向こうの外国為替の達人に渡す前に、まずabstractクラスにリファクタリングし、未実装のままにfindGoodDeals()します。

public abstract class ABCTradeSystem implements ITradeSystem{    

    public abstract void findGoodDeals();

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

株式取引システムfindGoodDeals()は、既に定義したとおりに実装されます。

public class StockTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

しかし今では、FX の達人はfindGoodDeals()、通貨の実装を提供するだけでシステムを構築できます。ソケット接続やインターフェイス メソッドを再実装する必要はありません。

public class CurrencyTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       ccys = <Genius stuff to find undervalued currencies>
       System.out.println("The best FX spot rates are: " + ccys);
    }

インターフェースへのプログラミングは強力ですが、同様のアプリケーションは、ほぼ同じ方法でメソッドを再実装することがよくあります。抽象クラスを使用すると、インターフェイスの機能を維持しながら再実装を回避できます。

注:findGreatDeals()なぜがインターフェースに含まれていないのか不思議に思うかもしれません。インターフェイスはトレーディング システムの最も一般的なコンポーネントを定義することを思い出してください。別のエンジニアは、良い取引を見つけることを気にしない完全に異なる取引システムを開発するかもしれません。インターフェースは、販売デスクがシステムにもインターフェースできることを保証するため、インターフェースを「お買い得」などのアプリケーションの概念と絡ませないことが望ましいです。

于 2016-12-20T15:57:30.593 に答える
7

抽象クラスとインターフェースのどちらを使用する必要がありますか?

これらのステートメントのいずれかがユース ケースに当てはまる場合は、抽象クラスの使用を検討してください。

密接に関連するいくつかのクラス間でコードを共有したい。

抽象クラスを拡張するクラスには、多くの一般的なメソッドまたはフィールドがあるか、public 以外のアクセス修飾子 (protected や private など) が必要であることが予想されます。

非静的または非最終フィールドを宣言したい。これにより、属するオブジェクトの状態にアクセスして変更できるメソッドを定義できます。

これらのステートメントのいずれかがユースケースに当てはまる場合は、インターフェイスの使用を検討してください。

無関係なクラスがインターフェイスを実装することを期待しています。たとえば、インターフェイス Comparable および Cloneable は、多くの無関係なクラスによって実装されます。

特定のデータ型の動作を指定したいが、その動作を誰が実装するかは気にしない。

型の多重継承を利用したい。

http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

于 2015-03-06T21:53:37.473 に答える
4

この 3 年間で、Java 8 リリースと連携する新しい機能が追加されたことで、状況は大きく変化しました。

インターフェイスのOracleドキュメントページから:

インターフェイスは、クラスと同様の参照型であり、定数、メソッド シグネチャ、既定のメソッド、静的メソッド、および入れ子になった型のみを含めることができます。メソッド本体は、デフォルト メソッドと静的メソッドに対してのみ存在します。

質問で引用したように、抽象クラスは、スケルトンを作成する必要があるテンプレート メソッド パターンに最適です。ここではインターフェイスを使用できません。

インターフェイスよりも抽象クラスを優先するためのもう 1 つの考慮事項:

基本クラスには実装がなく、サブクラスのみが独自の実装を定義する必要があります。サブクラスと状態を共有したいので、インターフェイスの代わりに抽象クラスが必要です。

抽象クラスは、関連するクラス間の「is a」関係を確立し、インターフェースは、関連のないクラス間の「has a」機能を提供します


質問の2番目の部分については、Java-8リリースより前のJavaを含むほとんどのプログラミング言語に有効です

常にトレードオフがあり、インターフェイスは基本クラスに関して自由を提供し、抽象クラスは後で新しいメソッドを追加する自由を提供します。– エーリッヒ・ガンマ

コード内の他の多くのものを変更することなく、インターフェイスを変更することはできません

上記の 2 つの考慮事項を考慮して、以前に抽象クラスをインターフェースすることを好む場合は、デフォルトのメソッドがインターフェースに強力な機能を追加したため、今考え直す必要があります。

既定のメソッドを使用すると、ライブラリのインターフェイスに新しい機能を追加し、それらのインターフェイスの古いバージョン用に記述されたコードとのバイナリ互換性を確保できます。

インターフェイスと抽象クラスの間でそれらのいずれかを選択するには、オラクルのドキュメントページで次のように引用しています。

抽象クラスはインターフェイスに似ています。それらをインスタンス化することはできず、実装の有無にかかわらず宣言されたメソッドが混在している可能性があります。ただし、抽象クラスを使用すると、静的でも最終でもないフィールドを宣言し、パブリック、プロテクト、およびプライベートの具象メソッドを定義できます。

インターフェイスを使用すると、すべてのフィールドが自動的に public、static、および final になり、(既定のメソッドとして) 宣言または定義するすべてのメソッドが public になります。さらに、抽象かどうかに関係なく、拡張できるクラスは 1 つだけですが、インターフェイスはいくつでも実装できます。

詳細については、次の関連する質問を参照してください。

インターフェイス vs 抽象クラス (一般的なオブジェクト指向)

Interface と Abstract クラスの違いをどのように説明すればよいでしょうか?

要約すると、現在、バランスはよりインターフェイスに傾いています

上記以外に、具体的に抽象クラスを使用する必要がある他のシナリオはありますか (1 つは、テンプレート メソッドの設計パターンが概念的にこれのみに基づいていることを確認します)。

一部の設計パターンは、テンプレート メソッド パターンとは別に、(インターフェイス上で) 抽象クラスを使用します。

作成パターン:

Abstract_factory_pattern

構造パターン:

Decorator_pattern

行動パターン:

Mediator_pattern

于 2016-03-18T18:46:04.447 に答える
3

私の意見では、基本的な違いはan interface can't contain non abstract methods while an abstract class can. したがって、サブクラスが共通の動作を共有している場合、この動作をスーパー クラスに実装して、サブクラスに継承することができます。

また、「Java でのソフトウェア アーキテクチャ デザイン ppatterns」の本から以下を引用しました。

" Java プログラミング言語では、多重継承はサポートされていません。つまり、クラスは 1 つのクラスからしか継承できません。したがって、継承は絶対に必要な場合にのみ使用する必要があります。可能な限り、共通の動作を示すメソッドを宣言する必要があります。異なる実装者クラスによって実装される Java インターフェースの形式. しかし, インターフェースには, メソッドの実装を提供できないという制限がある. これは, インターフェースのすべての実装者が, インターフェースで宣言されたすべてのメソッドを明示的に実装しなければならないことを意味する.メソッドは機能の不変部分を表し、すべての実装者クラスでまったく同じ実装を持っているため、コードが冗長になります。次の例は、冗長なメソッド実装を必要とせずに、このような場合に抽象親クラス パターンを使用する方法を示しています。」

于 2013-09-19T10:06:00.500 に答える
1

これは私の理解です、これが役立つことを願っています

抽象クラス:

  1. 継承されたメンバー変数を持つことができます (インターフェイスでは実行できません)
  2. コンストラクターを持つことができます (インターフェースはできません)
  3. そのメソッドには任意の可視性を持たせることができます (つまり、private、protected など - すべてのインターフェイス メソッドは public)
  4. 定義済みのメソッド (実装を伴うメソッド) を持つことができます

インターフェース:

  1. 変数を持つことができますが、それらはすべて public static final 変数です
    • 静的スコープで変更されない定数値
    • 非静的変数にはインスタンスが必要であり、インターフェースをインスタンス化することはできません
  2. すべてのメソッドは抽象的です (抽象メソッドにコードはありません)
    • すべてのコードは、特定のインターフェイスを実装するクラスで実際に記述する必要があります。
于 2016-10-03T16:45:55.683 に答える
1

これは良い質問です。これらの 2 つは似ていませんが、書き直しなど、同じ理由で使用できます。作成するときは、Interface を使用するのが最適です。クラスになると、デバッグに適しています。

于 2016-09-19T06:17:35.627 に答える
0

Abstract classes should be extended when you want to some common behavior to get extended. 抽象スーパークラスは共通の動作を持ち、サブクラスが実装する抽象メソッド/特定の動作を定義します。

Interfaces allows you to change the implementation anytime allowing the interface to be intact.

于 2012-04-06T06:54:50.877 に答える