18

私たちが知っているように、Interface クラスと Abstract クラスの間には基本的に 2 つの重要な違いがあります。

  1. 抽象クラスで関数定義を持つことができます。これは、すべての実装を追跡する必要なく、クラスに関数を追加したい場合に有利です。

  2. 複数のインターフェースを実装できます。

デカップリングの観点から、それらを区別できることを知りましたか?

あなたのコメント...

また、インターフェイスと抽象クラスのデカップリングを説明する非常に基本的なリンクを提供できますか?

通常、ビジネス ロジック レイヤーデータ アクセス レイヤー(抽象関数を含む)、およびDataAccess.SqlServer レイヤーを使用します。右?ビジネス ニーズを認識しているにもかかわらず、なぜデータ アクセス レイヤー(抽象関数を含む)を作成するのか、ビジネス ロジック レイヤーがDataAccess.SqlServer Layerに直接アクセスできないのはなぜですか?

4

9 に答える 9

19

デカップリング

プログラミングと設計では、これは通常、可能な限り少ない依存関係で再利用可能なコードを作成する行為です。

このコンテキストでのファクトリパターン

ファクトリパターンを使用すると、オブジェクトを定義しなくてもオブジェクトを作成できる一元化されたファクトリがあります。それはオブジェクトの定義次第です。

抽象とインターフェース

インターフェース

インターフェイスを定義することは、推論に軽量タイプを使用できるようにし、すべての継承クラスが従わなければならない青写真を提供するため、ベストプラクティスです。たとえば、メソッドIDisposableを実装する必要があります。継承する各クラスがメソッドの独自の関数を定義するため、Disposeこれはインターフェイスから切り離されていることに注意してください。IDisposableDispose

概要

Abstractは、継承と推論に使用されるという点でインターフェイスに似ていますが、すべてのクラスが継承する定義が含まれています。すべての自動車の範囲で何かがエンジンを持っているので、自動車の優れた抽象クラスには、エンジンの事前定義されたメソッドのセットを含めることができます。

編集

説明

ここでは、インターフェースと抽象クラスを使用した継承の簡単な例を示します。デカップリングは、インターフェイスが抽象クラスに継承され、そのメソッドがカスタマイズされたときに発生します。これにより、クラスは抽象クラスを継承し、インターフェースと同じタイプを維持できます。利点は、期待される型が元のインターフェースである場合に、抽象クラスを継承するクラスを使用できることです。

デカップリング

この利点により、期待されるインターフェースに準拠する任意の実装を使用できます。そのため、さまざまなオーバーロードを書き込んで渡すことができます。その一例を次に示します。

インターフェイス定義

public interface IReady
{
    bool ComputeReadiness();
}

継承

public abstract class WidgetExample : IReady
{
    public int WidgetCount { get; set; }
    public int WidgetTarget { get; set; }
    public bool WidgetsReady { get; set; }

    public WidgetExample()
    {
        WidgetCount = 3;
        WidgetTarget = 45;
    }

    public bool ComputeReadiness()
    {
        if (WidgetCount < WidgetTarget)
        {
            WidgetsReady = false;
        }
        return WidgetsReady;
    }
}


public class Foo : WidgetExample
{
    public Foo()
    {
        this.WidgetTarget = 2;
    }
}

public class Bar : IReady
{
    public bool ComputeReadiness()
    {
        return true;
    }
}

デカップリング

public class UsesIReady
{
    public bool Start { get; set; }
    public List<string> WidgetNames { get; set; }

    //Here is the decoupling. Note that any object passed
    //in with type IReady will be accepted in this method
    public void BeginWork(IReady readiness)
    {
        if (readiness.ComputeReadiness())
        {
            Start = true;
            Work();
        }
    }

    private void Work()
    {
        foreach( var name in WidgetNames )
        {
            //todo: build name
        }
    }
}

ポリモーフィズム

public class Main
{
    public Main()
    {
        //Notice that either one of these implementations 
        //is accepted by BeginWork

        //Foo uses the abstract class
        IReady example = new Foo();
        UsesIReady workExample = new UsesIReady();
        workExample.BeginWork(example);

        //Bar uses the interface
        IReady sample = new Bar();
        UsesIReady workSample = new UsesIReady();
        workSample.BeginWork(sample);
    }
}
于 2012-11-28T20:08:52.837 に答える
6

私は答えを見てきましたが、それらはすべて質問に対して少し複雑に思えます。だからここに私の(うまくいけば)より簡単な答えがあります。

  • 実装の詳細がコードの現在のスコープで利用できない場合は、インターフェイスを使用する必要があります。
  • 実装の詳細の一部が利用可能な場合は、要約を使用する必要があります。
  • そして、完全を期すために、すべての実装の詳細が利用可能になったら、classesを使用する必要があります。

デカップリングに関しては、Shelakel にある程度同意しますが、この質問の目的のために、完全にデカップリングされた設計手法を述べると、次のことをお勧めします。

  • 常にインターフェイスを使用して、外部の動作を定義します。
  • 実装の詳細を利用できる場合は、 抽象クラス使用してそれらを定義しますが、抽象クラスにインターフェイスを実装し、それらのクラスを順番に継承します。

これにより、後で新しい実装であいまいな実装の詳細を変更する必要がある場合に、既存の抽象クラスを変更せずに変更でき、異なる実装タイプを異なる抽象クラスにグループ化することもできます。

編集:リンクを含めるのを忘れました:) http://www.codeproject.com/Articles/11155/Abstract-Class-versus-Interface

于 2012-12-17T15:16:57.267 に答える
3

デカップリングのためのデカップリングは無駄な作業です。

インターフェイスは、詳細が有用であることがわかっている必要がない統合に使用することを目的としています(例:SendEmail())。一般的な用途には、コンポーネント、サービス、リポジトリ、およびIOCおよび一般的な実装のマーカーが含まれます。

インターフェイスを含むジェネリック型制約を持つ拡張メソッドは、Scalaに見られる特性と同様の機能を同様の構成可能性で可能にします。

public interface IHasQuantity { double Quantity { get; } }
public interface IHasPrice { decimal PricePerUnit { get; } }

public static class TraitExtensions
{
    public static decimal CalculateTotalPrice<T>(this T instance)
        where T : class, IHasPrice, IHasQuantity
    {
        return (decimal)instance.Quantity * instance.PricePerQuantity;
    }
}

私の意見では、抽象クラスとクラス継承は酷使されています。

SOLIDの設計原則は、リスコフの置換原則が、継承されたクラスが祖先の代わりに使用できる場合にのみクラスの継承を使用する必要があることを示しています。これは、すべてのメソッドを実装する必要があり(新しいNotImplementedExeption()をスローしない)、期待どおりに動作する必要があることを意味します。

私は個人的に、テンプレートメソッドパターンの場合やステートマシンの場合にクラスの継承が役立つことを発見しました。ほとんどの場合、ビルダーパターンなどのデザインパターンは、継承の深いチェーンよりも便利です。

さて、あなたの質問に戻りましょう。常にではないにしても、ほとんどの場合、インターフェイスを使用する必要があります。クラス継承は、定義の目的で内部および外部でのみ使用する必要があります。その後、インターフェイスを使用して、相互作用と、ファクトリを介して提供される具体的な実装、またはIOCコンテナを介して注入する必要があります。

理想的には、外部ライブラリを使用する場合は、必要な機能のみを公開するようにインターフェイスを作成し、アダプタを実装する必要があります。これらのコンポーネントのほとんどは、IOCコンテナを介して解決するために、事前にまたは実行時に構成することができます。

デカップリングに関しては、変更の理由を最小限に抑えるために、アプリケーションをその実装(特に外部依存関係)からデカップリングすることが重要です。

私の説明があなたを正しい方向に向けてくれることを願っています。動作中の実装をリファクタリングすることをお勧めします。その後、機能を公開するためにインターフェースが定義されます。

于 2012-12-16T19:24:50.870 に答える
3

抽象クラスとインターフェースは、相互排他的な選択肢ではありません。多くの場合、インターフェイスと、そのインターフェイスを実装する抽象クラスの両方を定義します。

インターフェイスは、クラスが特定の継承階層に属することを強制しないため、最大限の分離を保証します。したがって、クラスは他のどのクラスからも継承できます。つまり、どのクラスもインターフェイスから継承できますが、他のクラスから既に継承しているクラスは抽象クラスから継承できません。

一方、抽象クラスでは、すべての実装に共通するコードを除外できますが、インターフェイスでは、すべてを最初から実装する必要があります。結論として、多くの場合、最善の解決策は抽象クラスとインターフェイスの両方を使用することです。したがって、可能であれば、抽象クラスに含まれる共通コードを再利用することから、必要に応じてインターフェイスを最初から再実装することに移行できます。 .

于 2012-12-16T11:44:11.373 に答える
2

これら 2 つのコンストラクトの一般的な長所と短所については説明しません。十分なリソースがあるためです。

ただし、コンポーネントを別のコンポーネントから「分離」するという点では、インターフェイスの継承は、抽象クラスまたは一般的なクラス継承よりもはるかに優れています (実際、抽象的であろうとなかろうと、すべての分離に関して大きな違いはないと思います。abstractクラスが具体的な実装なしでインスタンス化されるのを防ぎます)。

上記の議論の理由は、インターフェイスを使用すると、「依存コンポーネント」が必要とする絶対最小値への露出を絞り込むことができるためです。単一のメソッドインターフェイスが必要な場合は、それを簡単に行うことができます。または、メソッドなしでマーカーインターフェイスにすることさえできます。基本クラス (抽象または具象) では、その基本のすべての「共通」機能を実装する必要があるため、これは難しい場合があります。このため、「基本型」に依存するコンポーネントは、その目的のためにそれらを必要としない場合でも、すべての共通機能を自動的に「参照」します。

インターフェイスはまた、共通点のないベースから継承するクラスであっても、インターフェイスを実装し、そのインターフェイスを期待するコンポーネントで使用できるため、最高の柔軟性を提供します。これの良い例はIDisposableインターフェイスです。

したがって、私の結論は、すべてのコンポーネントが基本型よりもインターフェイスに依存しているという懸念を切り離すことです。そのインターフェイスを実装するクラスのほとんどが共通の実装を持っていることがわかった場合は、そのインターフェイスを実装する基本クラスを持ち、その基本から他のクラスを継承します。

于 2012-12-12T04:36:03.600 に答える
1

違いは次のとおりです。

  • インターフェイスは、すべての子孫が順番に実装する必要がある0 個以上のメソッドシグネチャを公開します(そうしないと、コードはコンパイルさえしません)。インターフェイスに公開されたメソッドは、暗黙的に (インターフェイスから派生したすべての型がそれらにアクセスできます)、または明示的に (オブジェクトをインターフェイス型自体に型キャストする場合にのみメソッドにアクセスできます) 実装できます。詳細と例は、この質問にあります。

  • 抽象クラスは、0 個以上の完全なメソッドを公開します。これらのメソッドは、子孫使用またはオーバーライドして、独自の実装を提供できます。このアプローチにより、カスタマイズ可能な「デフォルト」の動作を定義できます。抽象クラスでは、問題なく新しいメソッドを簡単に追加できます (抽象クラスにメソッドを追加する場合は非常に優れています) が、インターフェイスにメソッドを追加するには、それを実装するすべてNotImplementedExceptionのクラスを変更する必要があります。

最後のポイントは、クラスは複数のインターフェースを同時に実装できるということです。実際の例は次のとおりです。

  • USB ポートと LAN ポートの両方を備えたハード ドライブは、複数のインターフェイスの継承を示す良い例です。
  • 「bluetooth」とマークされた LED が搭載されているが、Bluetooth ハードウェアが搭載されていないラップトップは、抽象メソッドを実装しないという概念の良い例えです (LED があり、小さな B 記号がありますが、屋根の下には何もありません)。

編集 1

インターフェイスとクラスの選択方法を説明するMSDN リンクを次に示します。

于 2012-12-12T08:35:16.673 に答える
1

抽象クラスを使用してコントラクトを定義するということは、実装者がこの抽象クラスから継承する必要があることを意味します。C# は多重継承をサポートしていないため、これらの実装者は別のクラス階層を持つことができず、一部の実装者にとってかなり制限される可能性があります。言い換えれば、抽象クラスは基本的に、(フレームワークまたはクラス ライブラリの) 他の機能を取得または使用するためにしばしば必要とされるクラス階層機能の実装者を奪います。

インターフェイスを使用してコントラクトを定義すると、クラス階層が自由になり、実装者は自由に使用できるようになります。つまり、実装の自由度が大幅に高まります。

評価基準の観点から、ここでカップリングについて話すとき、API/コントラクトを使用 (呼び出し) するクライアント、API/コントラクトの定義者、および API/コントラクトの実装者という 3 つの分離可能な作成者の懸念について話すことができます。 ; 自由(制限が少ないほど良い)、カプセル化(必要な意識が少ないほど良い)、そして変化に直面したときの回復力について話すことができます。

インターフェースは、実装者に提供される自由度が高いため、特に定義者と実装者の間で、抽象クラスよりも疎結合になると私は提案します。

一方、バージョン管理に関しては、追加されたメソッドが抽象クラスに実装されていれば、サブクラスの実装を必ずしも更新する必要なく、少なくとも別のメソッドを抽象クラスに追加できます。DLL の境界を越えてインターフェイスをバージョン管理することは、通常、別のインターフェイスを追加することを意味し、ロールアウトするのははるかに複雑です。(もちろん、すべての実装を一緒にリファクタリングできる場合、これは問題ではありません (たとえば、それらはすべて同じ DLL 内にあるため))。

于 2012-12-16T02:10:56.043 に答える
1

インターフェイス抽象クラスの違いを理解して覚える最善の方法は、抽象クラス通常のクラスであり、2 つの例外を除いて通常のクラスでできることはすべて抽象クラスでもできることを覚えておくことです

  1. 抽象クラスをインスタンス化することはできません
  2. 抽象クラスでのみ抽象メソッドを持つことができます
于 2015-02-06T08:45:50.397 に答える
0

インターフェイスへのコーディングは、再利用性とポリモーフィズムを提供します。クラスがインターフェイスを実装する限り、インターフェイスまたは抽象クラスは、インターフェイスを実装するクラスの代わりにパラメーターに渡すことができます。Urs の一般的な技術的問題は、インターフェイスと抽象クラスを設計して実装し、与えることで処理されます。特定の機能の実装をサブクラス化します。フレームワークのようなものを想像してください。フレームワークは、インターフェイスと抽象クラスを定義し、すべてに共通するものを実装します。抽象のものは、独自の要件に従ってクライアントによって実装されます。

public interface Polymorphism{
void run();
Void Bark();
Energy getEnergy(Polymorphism test);
Public abstract class EnergySynthesis implements Polymorphism{
abstract void Energy();
Void Bark(){
 getEnergy(){

}
void run(){
getEnergy();

}public EnegyGeneartion extends EnergySynthesis  {
Energy getEnergy(Polymorphism test){
return new Energy( test); 
}
MainClass{

EnegyGeneartion test=new EnegyGeneartion ();
test.getEnergy(test);
test.Bark()
.
.
.
.
.
//here in Energy getEnergy(Polymorphism test) any class can be passed as parameter that implemets interface 
于 2012-12-19T03:24:58.647 に答える