疎結合を実現するために、インターフェイスがスーパークラスよりも役立つのはなぜですか?


15

この質問の目的のために、私が「インターフェース」と言うとき、私は言葉の他の意味での「インターフェース」ではなく、言語構成体を意味しinterfaceます、すなわち、クラスが通信し、操作します。

オブジェクトを具象型ではなく抽象化に依存させることで、疎結合を実現できます。

これは、2つの主な理由のための疎結合することができます:1 -抽象化が依存するコードが少なく破るためにある意味、具体的なタイプより変更になりにくいです。2 -彼らはすべての抽象化をフィットするので別の具体的なタイプは、実行時に使用することができます。既存の依存コードを変更する必要なしに、新しいコンクリートタイプを後で追加することもできます。

たとえば、あるクラスCarと2つのサブクラスVolvoとを考えMazdaます。

コードがに依存している場合、ランタイム中にCara Volvoまたはaを使用できMazdaます。また、後で追加のサブクラスを追加して、依存コードを変更する必要はありません。

また、Car-抽象化である-は、Volvoまたはよりも変更される可能性が低いMazdaです。車はかなり以前から一般的に同じでしたが、ボルボとマツダははるかに変わる可能性が高いです。すなわち、抽象化は具体的な型よりも安定しています。

これはすべて、疎結合とは何か、そして具体的ではなく抽象化に依存することによってどのように達成されるかを理解していることを示すことでした。(不正確なものを書いた場合はそう言ってください)。

私が理解していないのはこれです:

抽象化は、スーパークラスまたはインターフェースにすることができます。

もしそうなら、なぜインターフェイスは疎結合を可能にする能力で特に称賛されていますか?スーパークラスを使用することとどう違うかわかりません。

私が見る唯一の違いは次のとおりです。1-インターフェースは単一継承によって制限されませんが、それは疎結合のトピックとはあまり関係がありません。2-インターフェースには実装ロジックがないため、より「抽象的」です。しかし、それでも、なぜそんなに大きな違いがあるのか​​わかりません。

単純なスーパークラスはそうではないが、インターフェイスが疎結合を可能にするのに優れていると言われている理由を説明してください。


3
「インターフェース」を持つほとんどの言語(Java、C#など)は、単一の継承のみをサポートします。各クラスは1つの直接スーパークラスしか持つことができないため、1つのオブジェクトが複数の抽象化をサポートするには(抽象)スーパークラスが制限されすぎます。多重継承による「ダイアモンドの問題」を回避する最新の代替手段については、特性(ScalaPerlの役割など)を確認してください。
アモン14

@amon疎結合を達成しようとするとき、抽象クラスに対するインターフェースの利点は、単一の継承によって制限されないということですか?
アビブコーン14

いいえ、私は意味高価な面で、コンパイラ、それは抽象クラスを扱う際に行うことが多くを持っているが、これはおそらく無視することができます。
ペースト状14

2
@amonは正しい軌道に乗っているように見え、私が見つかりました。この記事:と言われてinterfaces are essential for single-inheritance languages like Java and C# because that's the only way in which you can aggregate different behaviors into a single class(インターフェイスは、純粋仮想関数を持つだけのクラスであるC ++との比較に私をリーズたが)。
ペースト状14

スーパークラスが悪いと言う人を教えてください。
Tulainsコルドバ

回答:


11

用語:言語構造interfaceinterfaceと呼び、型またはオブジェクトのインターフェースをsurfaceと呼びます(より良い用語がないため)。

オブジェクトを具象型ではなく抽象化に依存させることで、疎結合を実現できます。

正しい。

これは2つの主な理由のための疎結合を可能にする1 -抽象化が依存するコードが少なく破壊することを意味し、具体的なタイプよりも変化しにくくなります。2-抽象に適合するため、実行時にさまざまな具象型を使用できます。既存の依存コードを変更する必要なしに、新しいコンクリートタイプを後で追加することもできます。

まったく正しくありません。現在の言語は、一般に抽象化が変更されることを予期していません(ただし、それを処理する設計パターンはいくつかあります)。一般的なものから詳細を分離すること抽象化です。これは通常、何らかの抽象化層によって行われます。この抽象化に基づいて構築されたコードを壊すことなく、このレイヤーを他のいくつかの詳細に変更できます。疎結合が実現されます。非OOPの例:sortルーチンは、バージョン1のQuicksortからバージョン2のTim Sortに変更される可能性があります。sortしたがって、ソートされる(つまり抽象化に基づいて)結果にのみ依存するコードは、実際のソートの実装から切り離されます。

私はと呼ばれる表面を上にすることで、一般的な一部の抽象化。OOPでは、1つのオブジェクトが複数の抽象化をサポートする必要がある場合があります。非常に最適ではない例:Java は、「順序付けられたインデックス可能なコレクション」抽象化に関するインターフェイスと、(大まかに言えば)「FIFO」抽象化に関するインターフェイスのjava.util.LinkedList両方ListをサポートしQueueます。

オブジェクトは複数の抽象化をどのようにサポートできますか?

C ++にはインターフェイスはありませんが、複数の継承、仮想メソッド、および抽象クラスがあります。抽象化は、仮想メソッドを宣言するが定義はしない抽象クラス(つまり、すぐにインスタンス化できないクラス)として定義できます。抽象化の詳細を実装するクラスは、その抽象クラスから継承して、必要な仮想メソッドを実装できます。

ここでの問題は、多重継承がひし形の問題を引き起こす可能性があることです。そこでは、クラスがメソッド実装を検索する順序(MRO:メソッド解決順序)が「矛盾」につながる可能性があります。これには2つの応答があります。

  1. 正しい順序を定義し、合理的に線形化できない順序を拒否します。C3 MROはかなり賢明であるとうまく動作します。1996年に公開されました。

  2. 簡単なルートをたどり、全体を通して複数の継承を拒否します。

Javaは後者のオプションを採用し、単一の動作継承を選択しました。ただし、複数の抽象化をサポートするには、オブジェクトの機能が必要です。したがって、メソッド定義をサポートせず、宣言のみをサポートするインターフェイスを使用する必要があります。

その結果、MROは明らかで(各スーパークラスを順番に見るだけで)、オブジェクトは任意の数の抽象化に対して複数のサーフェスを持つことができます。

非常に多くの場合、動作の一部が表面の一部であるため、これはかなり不十分であることがわかります。Comparableインターフェイスを考えてみましょう:

interface Comparable<T> {
    public int cmp(T that);
    public boolean lt(T that);  // less than
    public boolean le(T that);  // less than or equal
    public boolean eq(T that);  // equal
    public boolean ne(T that);  // not equal
    public boolean ge(T that);  // greater than or equal
    public boolean gt(T that);  // greater than
}

これは非常にユーザーフレンドリーです(多くの便利なメソッドを持つ素晴らしいAPI)が、実装するのは面倒です。インターフェイスにはのみを含めcmp、他のメソッドはその1つの必須メソッドに関して自動的に実装するようにします。Mixins、しかしより重要なことは、Traits [ 1 ]、[ 2 ]は多重継承のofに陥ることなくこの問題を解決します。

これは、特性が実際にMROに参加しないように特性構成を定義することで行われます。代わりに、定義されたメソッドが実装クラスに構成されます。

Comparableインタフェースは、Scalaのように表すことができ

trait Comparable[T] {
    def cmp(that: T): Int
    def lt(that: T): Boolean = this.cmp(that) <  0
    def le(that: T): Boolean = this.cmp(that) <= 0
    ...
}

クラスがその特性を使用すると、他のメソッドがクラス定義に追加されます。

// "extends" isn't different from Java's "implements" in this case
case class Inty(val x: Int) extends Comparable[Inty] {
    override def cmp(that: Inty) = this.x - that.x
    // lt etc. get added automatically
}

そうInty(4) cmp Inty(6)だろう-2Inty(4) lt Inty(6)なりますtrue

多くの言語は特性をある程度サポートしており、「メタオブジェクトプロトコル(MOP)」を持つ言語は特性を追加できます。最近のJava 8アップデートでは、特性に似たデフォルトのメソッドが追加されました(インターフェースのメソッドはフォールバック実装を持つことができるため、これらのメソッドを実装するクラスを実装するためのオプションです)。

残念ながら、形質はかなり最近の発明(2002年)であるため、大規模な主流言語ではかなりまれです。


良い答えですが、単一継承言語、構成のインターフェイスを使用して複数の継承を妨害する可能性があると付け加えます。

4

私が理解していないのはこれです:

抽象化は、スーパークラスまたはインターフェースにすることができます。

もしそうなら、なぜインターフェイスは疎結合を可能にする能力で特に称賛されていますか?スーパークラスを使用することとどう違うかわかりません。

まず、サブタイピングと抽象化は2つの異なるものです。サブタイプとは、単に、あるタイプの値を別のタイプの値に置き換えることができることを意味します。どちらのタイプも抽象である必要はありません。

さらに重要なことに、サブクラスはスーパークラスの実装の詳細に直接依存しています。それが存在する最強のカップリングです。実際、基本クラスが継承を念頭に置いて設計されていない場合、その動作を変更しない基本クラスへの変更によりサブクラスが破損する可能性があり、破損が発生するかどうかを事前に知る方法はありません。これは、壊れやすい基本クラスの問題として知られています

インターフェースを実装しても、動作を含まないインターフェース自体以外には何も結合されません。


回答ありがとうございます。私が理解しているかどうかを確認するには:Aという名前のオブジェクトを、Cという名前の抽象化の具体的な実装ではなく、Bという名前の抽象化に依存させたい場合は、Bがスーパークラスではなく、 C.これは、CがBをサブクラス化することにより、CをBに緊密に結合します。ただし、Bを実装するC(Bはインターフェイス)はBをCに結合しません。BはCが実装しなければならないメソッドのリストにすぎないため、密結合はありません。ただし、オブジェクトA(依存)に関しては、Bがクラスであるかインターフェイスであるかは関係ありません。
アビブコーン14

正しい?..... ....。
アビブコーン14

インターフェースを何かに結合すると考えるのはなぜですか?
マイケルショー14

この答えは頭に釘付けだと思います。私はC ++をかなり使用しており、他の回答の1つで述べたように、C ++にはインターフェースがありませんが、すべてのメソッドを「純粋仮想」(つまり、子によって実装)のままにしたスーパークラスを使用して偽造します。ポイントは、委任された機能とともに何かを行う基本クラスを簡単に作成できることです。多くの場合、多くの場合、私と同僚は、それを行うことで、新しいユースケースが登場し、共有された機能の一部を無効にすることに気付きました。共有機能が必要な場合、ヘルパークラスを作成するのは簡単です。
Jトラナ14

@Progあなたの考え方はほとんど正しいですが、やはり、抽象化とサブタイピングは2つの別個のものです。あなたが言うとき、あなたはyou want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named Cクラスが何らかの形で抽象的ではないと仮定している。抽象化は、実装の詳細を隠すものであるため、プライベートフィールドを持つクラスは、同じパブリックメソッドを持つインターフェイスと同じくらい抽象的です。
ドーバル14

1

子は親に依存するため、親クラスと子クラスの間にはカップリングがあります。

クラスAがあり、クラスBがそれを継承するとします。クラスAに入って変更すると、クラスBも変更されます。

インターフェイスIがあり、クラスBがそれを実装しているとします。インターフェイスIを変更した場合、クラスBはそれを実装しない可能性がありますが、クラスBは変更されていません。


ダウンボッターが理由を持っているのか、それともただ悪い一日を過ごしていたのか、私は知りたいです。
マイケルショー14

1
私は下票しませんでしたが、最初の文と関係があると思います。子クラスは親クラスに結合されますが、その逆ではありません。親は子供について何も知る必要はありませんが、子供は親について親密な知識を必要とします。

@JohnGaughan:フィードバックをありがとう。明確にするために編集されました。
マイケルショー14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.