15

重複の可能性:
インターフェースと基本クラス

インターフェイスを使用して実装されたリポジトリパターンを確認するのが一般的です

public interface IFooRepository
{
   Foo GetFoo(int ID);
}

public class SQLFooRepository : IFooRepository
{
   // Call DB and get a foo
   public Foo GetFoo(int ID) {}
}

public class TestFooRepository : IFooRepository
{
   // Get foo from in-memory store for testing
   public Foo GetFoo(int ID) {}
}

しかし、抽象クラスを使用してこれを同様に行うことができます。

public abstract class FooRepositoryBase
{
    public abstract Foo GetFoo(int ID);
}

public class SQLFooRepository : FooRepositoryBase
{
    // Call DB and get a foo
    public override Foo GetFoo(int ID); {}
}

public class TestFooRepository : FooRepositoryBase
{
    // Get foo from in-memory store for testing
    public override Foo GetFoo(int ID); {}
}

リポジトリシナリオで抽象クラスよりもインターフェイスを使用することの具体的な利点は何ですか?

(つまり、複数のインターフェースを実装できるとだけ言ってはいけません。これはすでに知っています。なぜリポジトリの実装でそれを行うのでしょうか)

明確にするために編集-「 MSDN-クラスとインターフェイスの選択」のようなページは、「適切な理由がない限り、インターフェイスからクラスを選択する」と言い換えることができます-リポジトリパターンの特定の場合の適切な理由は何ですか

4

7 に答える 7

4

Foo GetFoo()個人的には、など、純粋に「ビジネス関連」のメソッドのシグネチャを保持するインターフェイスを使用する傾向があります。またはなどvoid DeleteFood(Foo foo)の保護されたメソッドを保持する汎用抽象クラスもあります。T Get()void Delete(T obj)

私はメソッドを抽象クラスで保護し、外部の世界が配管(のように見える)をRepository認識せず、インターフェイスを介したビジネスモデルのみを認識できるようにします。Repositoryobject

配管を共有することに加えて、別の利点は、たとえば、任意のリポジトリで使用可能なDeleteメソッド(protected)があるが、公開されていないため、ビジネス上の意味がないリポジトリに実装する必要がないことです。情報源。

public abstract class Repository<T>
{
    private IObjectSet objectSet;

    protected void Add(T obj)
    {
        this.objectSet.AddObject(obj);
    }

    protected void Delete(T obj)
    {
        this.objectSet.DeleteObject(obj);
    }

    protected IEnumerable<T>(Expression<Func<T, bool>> where)
    {
        return this.objectSet.Where(where);
    }
}

public interface IFooRepository
{
    void DeleteFoo(Foo foo);
    IEnumerable<Foo> GetItalianFoos();
}

public class FooRepository : Repository<Foo>, IFooRepository
{
    public void DeleteFoo(Foo foo)
    {
        this.Delete(foo);
    }

    public IEnumerable<Foo> GetItalianFoos()
    {
        return this.Find(foo => foo.Country == "Italy");
    }
}

配管のインターフェースを介して抽象クラスを使用する利点は、私の具体的なリポジトリーが必要のないメソッド(DeleteまたはAddたとえば)を実装する必要がないことですが、必要に応じて自由に使用できます。現在のコンテキストでは、一部のFoosにはビジネス上の理由がないため、このメソッドはインターフェイスで使用できません。

ビジネスモデルの抽象クラスよりもインターフェイスを使用する利点は、インターフェイスがFooビジネス側から操作するのが理にかなっている方法に対する答えを提供することです(いくつかのfooを削除するのは理にかなっていますか?いくつかを作成するのですか?など)。単体テストの際にも、このインターフェースを使用する方が簡単です。私が使用するアブストラクトRepositoryは、通常データベースと緊密に結合されているため、ユニットテストできません。統合テストでのみテストできます。リポジトリのビジネス目的で抽象クラスを使用すると、単体テストでそれらを使用できなくなります。

于 2012-07-04T10:09:29.493 に答える
4

この場合、抽象クラスに対してインターフェースを使用する主な利点は、インターフェースが完全に透過的であることです。これは、継承元のクラスのソースにアクセスできない場合の問題です。

ただし、この透過性により、既知のスコープの単体テストを作成できます。インターフェイスをパラメーターとして受け入れるクラスを(依存性注入メソッドを使用して)テストする場合、既知の量でクラスをテストしていることがわかります。インターフェイスのテスト実装には、テストコードのみが含まれます。

同様に、リポジトリをテストするときは、リポジトリ内のコードだけをテストしていることがわかります。これは、テストで可能な変数/相互作用の数を制限するのに役立ちます。

于 2012-07-04T10:46:19.410 に答える
3

これは、リポジトリだけでなく、すべてのクラス階層に当てはまる一般的な質問です。純粋なOOの観点からは、インターフェースと純粋な抽象クラスは同じです。

クラスがパブリックAPIの一部である場合、抽象クラスを使用する主な利点は、既存の実装を壊すリスクがほとんどなく、将来的にメソッドを追加できることです。

一部の人々はまた、インターフェースを「クラスが実行できること」として定義し、基本クラスを「クラスとは何か」として定義することを好みます。したがって、周辺機能のインターフェースのみを使用し、常にプライマリ機能(リポジトリなど)を次のように定義します。クラス。私はこれのどこに立っているのかわかりません。

あなたの質問に答えるために、クラスの主要な機能を定義するときにインターフェースを使用することに利点はないと思います。

于 2012-07-04T10:21:08.917 に答える
2

他の人はもっと追加する必要があるかもしれませんが、純粋に実用的な観点から、ほとんどのIoCフレームワークはインターフェース->クラスマッピングでよりうまく機能します。インターフェイスとクラスで異なる可視性を持つことができますが、継承では、可視性が一致する必要があります。

IoCフレームワークを使用していない場合、私の観点からは違いはありません。プロバイダーは、抽象基本クラスに基づいています。

于 2012-07-04T10:07:43.157 に答える
2

主な違いは、抽象クラスにはプライベートプロパティとメソッドを含めることができるのに対し、インターフェイスは単純なコントラクトにすぎないため、含めることができないことだと思います。

インターフェースである結果は常に「ここにシェナニガンはありません-あなたが見るものはあなたが得るものです」が、抽象的な基本クラスは副作用を許すかもしれません。

于 2012-07-04T10:13:13.540 に答える
2

パターンはドメイン駆動設計に由来するため、DDDの回答は次のとおりです。

リポジトリのコントラクトは通常、ドメイン層で定義されます。これにより、ドメイン層とアプリケーション層のオブジェクトは、実際の実装や基盤となるストレージの詳細を気にすることなく、リポジトリの抽象化を操作できます。つまり、永続性を無視できます。さらに、特定の動作を(バニラAdd()、GetById()などに加えて)一部のリポジトリのコントラクトに含めることがよくあるので、私ISomeEntityRepositoryは単なるフォームよりもフォームを好みIRepository<SomeEntity>ます-なぜそれらが必要なのかを見ていきます後でインターフェースします。

一方、リポジトリの具体的な実装は、インフラストラクチャ層(またはテストリポジトリのテストモジュール)にあります。これらは上記のリポジトリコントラクトを実装しますが、独自の範囲の永続性固有の特性も備えています。たとえば、NHibernateを使用してエンティティを永続化する場合、NHibernateセッションおよびその他のNHibernate関連の汎用配管を含むすべてのNHibernateリポジトリへのスーパークラスがあると便利です。

複数のクラスを継承することはできないため、最終的な具象リポジトリが継承するこれら2つのものの1つはインターフェースである必要があります。

ドメインレイヤーコントラクトがインターフェイス(ISomeEntityRepository)である方が論理的です。これは、純粋に宣言型の抽象化であり、使用される永続メカニズムについて何も想定してはならないためです。つまり、何も実装してはなりません。

永続性固有のものは、抽象クラス(NHibernateRepositoryまたはNHibernateRepository<T>インフラストラクチャ層)にすることができます。これにより、存在する永続ストア固有のリポジ​​トリの全範囲に共通するいくつかの動作を一元化できます。

これにより、次のようになります。

public class SomeEntityRepository : NHibernateRepository<SomeEntity>, ISomeEntityRepository 
{
  //...
}
于 2012-07-04T14:47:51.947 に答える
1

TimMcCarthyのリポジトリフレームワークの実装を見てください。< http://dddpds.codeplex.com/ >

彼はコントラクトを定義するためのようなインターフェースを使用しますが、を実装するまたは彼のIRepository<T>ような抽象クラスも使用します。抽象基本クラスは、多くの重複コードを排除するためのコードです。タイプ固有のリポジ​​トリは、抽象基本クラスから継承する必要があり、その目的のためにコードを追加する必要があります。APIのユーザーは、コントラクトによってインターフェースに対してコーディングすることができます。RepositoryBase<T>SqlCeRepositoryBase < T >IRepository<T>

したがって、両方のアプローチを組み合わせて、それらの利点を利用できます。

さらに、ほとんどのIoC-Frameworksは抽象クラスを処理できると思います。

于 2012-07-04T10:40:48.720 に答える