2009-03-04 4 views
0

私は常に私が追加しているクラスを持っています。クラスをOCPに従うようにする - ファクタリング関数をオブジェクトに変換する

これらの新機能がすべて追加されているため、このクラスはオープンクローズではありませんでした。だから私は、これらの関数を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>(); 
    } 
} 

これは良い最初のステップでした。しかし、要求プロセッサは、それが格納している正確なタイプのコンテキストを必要とします。私はここにはありません。私は型に種類の辞書を使用することができますが、それは私のような何かをする必要が終わるオープン・クローズ。だからであることの目的に反し:

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

これは奇妙であると私は普通のが好きじゃありませんcuriously recurring template pattern。さらに、コンテキストを埋めるユーザに、それを要求するように依頼するという考えは、ちょうど命名の問題かもしれないが、奇妙に思える。悪いことではありません

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

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

を、しかし、APIは醜いです:

3)私は上記の、ちょうど持っていることのすべてを取り除くことを考えました。今、このオブジェクトのクライアントは、それを2回「構築」する必要があります。 PackArgsを呼び出すことを忘れてしまった場合は、何らかの例外をスローする必要があります。

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

答えて

0

アエデンデは、this subjectfew postsを持っています。

基本的には、リポジトリからクエリを切り離し、クエリをに置き換えます。コンポジションで作成されたクエリでは、リポジトリに新しいメソッドを追加しなくても簡単にクエリを拡張することができます。私は実際にはまだNHibernateのでこれを行っていないが、我々はLLBLGenProでこれを行なったし、それが本当にうまくいった

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

:あなたはこのようなもので終わることができます。私たちは、クエリは、私たちはこのようなクエリ条件を書くことができオブジェクトのための流れるようなインターフェイスを使用:

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

var employee = repository.Find(query); 

は、リポジトリの機能を拡張するだけでクエリオブジェクトに新しいメソッドを追加することでした。

+0

これはやや役立ちますが、私の質問はリクエストをカプセル化し、クエリではないことです。さらに、私はリポジトリの背後にあるDBにアクセスしていませんが、おそらく問題ではありません。 – DavidN

1

リポジトリはあなたのドメインの一部です。ユビキタスな言語での操作の本拠地としての目的を打ち破っています。 にリポジトリがある場合は、その意図をわかりにくくしています。

クラスがSRPに従う場合、「常に追加する」というのは定義に違反します。これは、導入している操作がサービスによってより適切に処理されるか、そうでなければリポジトリから切り離されるべきであることを示します。あなたが確実にクラスが責任の最小量を持っていながら、オープンであなたのユビキタス言語を維持したい

をコメントに応答して

編集。

リポジトリからの操作の中断は、最初の手順です。

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

public interface IOrderDataService 
{ 
    void GetOrderData(Order order, DateTime start, DateTime end); 
} 
+0

"これは、導入している操作がサービスによってより適切に処理されるか、そうでなければリポジトリから切り離されるべきであることを示しています。 - それは私がやろうとしてきたことです。あなたはデカップリング部分について何か提案していますか? – DavidN

+0

偉大な答え。私はこれらのサービスをクラスとして実装します。単体テストやそれに依存するクラスさえ、はるかに簡単で、しかも簡単です。 –

+0

私は、インタフェースを使用するのではなく、抽象クラスを使用してインタフェースを表現することを選択しました。それぞれ自分自身に。 –

関連する問題