4

リポジトリを持っているとしましょう:

interface IRepo
{
    Foo Get(int id);
    void Add(Foo f);
}

さて、指定されたプロパティを持つものだけを持つことができるという要件がありますFoo.... Id明らかに、IRepoSQL バックエンドを実装して主キーにマップFoo.Idする場合、このようなものを無料で取得できます。おそらく何らかのPKViolationException. しかし、これは実装固有になりつつあるため、このIRepo実装を使用してそれらの例外をキャッチし始めると、疎結合の利点が失われます。

さて、どうすればこれを修正できますか?最初にオブジェクトが存在するかどうかを確認し、次にリポジトリに依存しない例外をスローするサービス層を追加する必要がありますか?

class Service
{
    IRepo repo;

    public void AddFoo(Foo foo)
    {
        if(repo.Get(foo.Id) != null)
            repo.Add(foo);
        else
            throw new FooAlreadyExistsException(foo.Id);
    }
}

repo.Add(foo)特に特定の Id を持つオブジェクトがチェックの直後に (他のアクティビティによって) 追加された場合は例外がスローされる可能性があるため、このソリューションは悪いように思えます。

このようなIRepo例外を念頭に置いて注意して実装する必要があるようです (SQL 実装の例でキャッチPKViolationExceptionして変換することができます) が、すべての実装がそのような仕様に従っFooAlreadyExistsExceptionていることを確認するにはどうすればよいですか?IRepo

一般的に、これらの問題にどのように取り組んでいますか?

4

3 に答える 3

4

「注意して、そのような例外を念頭に置いて IRepo を実装する必要があるようです (SQL 実装の例では、PKViolationException をキャッチして FooAlreadyExistsException に変えることができます)」

あなたはこれでお金を稼いでいます。スローされる例外はインターフェイス コントラクトの一部になり、実装はこのコントラクトに従う必要があります。コンパイラはこれを強制しないので、期待について完全に明確にする必要があります。

「しかし、すべての IRepo 実装がそのような仕様に従っていることを確認するにはどうすればよいでしょうか?」

インターフェイスの作成者として、それを実装するクラスについて責任を負うことはできません。他のクラスに欠陥があり、漏れやすい抽象化を公開している場合、それが欠陥です。できることは、定義している契約について完全に明確にすることだけです。

リポジトリの目的は、例外を含むデータベースの実装の詳細を抽象化することです。したがって、実装固有の例外も抽象化する必要があります...

interface IRepo
{
    /// <exception cref="FooAlreadyExistsException">Thrown if the specified Foo object already exists in the database.</exception>
    void Add(Foo f);
}

class ConcreteRepo
{
    public void Add(Foo f)
    {
        try
        {
            // Database stuff...
        }
        catch (PKViolationException e)
        {
            throw new FooAlreadyExistsException(e);
        }
    }
}
于 2012-10-29T10:49:08.500 に答える
3

まず、次の質問について考えてみてください: リポジトリで使用されている基になるバッキング ストアを変更する (つまり、主キーを持つデータベースから、一意の ID の概念を持たない未知のデータストアに変更する) 確率はどのくらいですか? 確率は 0 に非常に近いと思います。YAGNI がここに適用されます。非常にありそうもない未知の変更が発生した場合に備えて、コードの実装を開始しないでください。変更が実際に発生したら、必要な変更を行います。

次に、あなたの例に関して、クライアントコードに以前にそれを強制するのではなく、追加されるエンティティの新しい ID を生成しないでください (または、基礎となるデータストアにそれをさせてください)。ID は、データベース シーケンス、UUID、または n 自動インクリメント カラムを使用して自動生成する必要があります。

于 2012-10-28T12:28:26.077 に答える
2

最初に言っておきますが、

repo.Add(foo) は、特に特定の ID を持つオブジェクトがチェックの直後に (他のアクティビティによって) 追加された場合に、何らかの例外をスローする可能性があります。

オーバーエンジニアリングの一部です。

Add メソッドが多くの検証を行う必要があると強く感じている場合は、メソッドに TryAddのようにTryParseのような名前を付けます。そうすることで、Add メソッドの呼び出し元は、値を追加する前に Add メソッドが検証を行うことを認識します。

于 2012-10-29T10:41:06.603 に答える