0

私は常に追加しているクラスを持っています。

public class OrderRepository{
    public void Add(IEnumerable<Order> orders){}
    public void Update(IEnumerable<Order> orders){}
    public void Remove(IEnumerable<Order> orders){}
    public void Expedite(IEnumerable<Order> orders){}
    public void GetOrderData(Order order, DateTime start, DateTime end)
    etc...
}

これらすべての新機能が追加されているため、このクラスは開閉式ではないことに気付きました。そこで、これらの関数を Request オブジェクトにカプセル化することで、この変更に対してこのクラスを閉じることを考えました。私は次のようなものになります:

public abstract class RequestBase{}

public class AddRequest : RequestBase{}

etc...

public class OrderRepository{
    public void ProcessRequest(RequestBase request){}
}

これにより、 OrderRepository が拡張用に開かれ、変更用に閉じられます。ただし、これに関していくつかの問題にすぐに遭遇しました。

1.) リクエストが操作する必要があるデータは、ユーザー提供 (実行時) と依存性注入提供の両方です。明らかに、1 つのコンストラクターで両方を満たすことはできません。私はできません:

public class AddRequest{
    public AddRequest(IEnumerable<Order> orders, int UserSuppliedContextArg1, DependencyInjectionArg1, DependencyInjectionArg2);
}

そしてそれを呼び出します。DIフレームワークがオブジェクトを「部分的に」構築し、残りは私に任せる方法が必要です。しかし、それを行う方法はありません。この概念を「変数コンストラクター インジェクション」と呼んだブログを見ました。

2.) 次に考えたのは、これを 2 つの別々のクラスに分割することでした。ユーザーは RequestContext を作成して入力し、それをリポジトリに渡します。リポジトリは、そこから RequestProcessor (より適切な名前を思いつきません) を作成します。私は次のことを考えました:

public abstract class RequestContextBase<T> where T : RequestProcessorBase{}

public class AddRequestContext : RequestContextBase<AddRequestProcessor>

public class OrderRepository{
    public void ProcessRequest<T>(RequestBase<T> request){
        var requestProcessor = IoC.Create<T>();
    }
}

それは良い第一歩でした。ただし、リクエスト プロセッサには、保存している正確なタイプのコンテキストが必要ですが、ここにはありません。型の辞書を型に使用することもできますが、それは Open-Closed であるという目的を無効にします。そのため、次のようなことをしなければならなくなります。

public class RequestProcessorBase<TRequestContext, TRequestProcessorBase> where TRequestContext : RequestContextBase<TRequestProcessorBase>

これは奇妙で、私は通常、奇妙に繰り返されるテンプレート パターンが好きではありません。さらに、名前付けの問題かもしれませんが、ユーザーがコンテキストを埋めて、それをリクエストするように私に依頼するという考えは奇妙に思えます。

3.) 上記のすべてを取り除き、次のものを用意することを考えました:

public AddRequest{
    public AddRequest(DependencyInjectionArg1, DependencyInjectionArg2, ...){}

    public void PackArgs(UserSuppliedContextArg1, UserSuppliedContextArg2, UserSuppliedContextArg3, ...){}
}

これは悪くありませんが、API は醜いです。このオブジェクトのクライアントは、いわばそれを 2 回「構築」する必要があります。また、PackArgs を呼び出すのを忘れた場合は、何らかの例外をスローする必要があります。

続けることもできますが、これらは現時点で最も混乱している問題です。何か案は?

4

2 に答える 2

1

リポジトリはドメインの一部です。それを一般的なものにすることは、魅力的ではありますが、ユビキタス言語での運用の拠点としての目的を打ち破ります。リポジトリで何でもできるなら、その意図を曖昧にしていることになります。

クラスがSRPに続く場合、定義上、「常に追加する」ことはそれに違反します。これは、導入している操作がサービスによってより適切に対処される可能性があるか、そうでなければリポジトリから分離されるべきであることを示しています。

コメントに応じて編集する

クラスの責任を最小限に抑えながら、ユビキタスな言語を公開しないようにします。

リポジトリから操作を分割することが最初のステップになります。あなたはこのようなことをすることができます:

public interface IOrderExpeditionService
{
    void Expedite(IEnumerable<Order> orders);
}

public interface IOrderDataService
{
    void GetOrderData(Order order, DateTime start, DateTime end);
}
于 2009-03-04T04:28:43.603 に答える
0

Ayende には、このテーマに関する投稿がいくつかあります。

基本的にやりたいことは、クエリをリポジトリから切り離し、クエリを作成するものに変えることです。コンポジションによって構築されたクエリを使用すると、簡単に拡張して、リポジトリに新しいメソッドを追加することなく、クエリに新しい方法を追加できます。次のような結果になる可能性があります。

public class Repository<T>
{
   T Find(IQueryCriteria queryCriteria);
}

NHibernate で実際にこれを行ったことはまだありませんが、LLBLGenPro でこれを行ったところ、非常にうまく機能しました。クエリ オブジェクトに流暢なインターフェイスを使用したため、クエリ条件を次のように記述できます。

var query = new EmployeeQuery()
   .WithLastName("Holmes")
   .And()
   .InDepartment("Information Systems");

var employee = repository.Find(query);

リポジトリの機能を拡張すると、単純にクエリ オブジェクトに新しいメソッドを追加することになります。

于 2009-03-04T02:22:26.977 に答える