2016-12-07 10 views
0

私は直面している設計上の問題を解決する良い解決策を見つけることができません。現在、私はクエリ文字列を解析するシステムを設計しています。汎用/継承に関する設計上の問題

各パーサは、インタフェースIQueryParserを実装する必要があります。私は、HTTPリクエストを受信すると

public interface IQueryParser 
{ 
    Query Parse(); 
} 

、私は工場を使用してそれを解析することができパーサを探してみてください。 QueryContextはHttpRequestの

public interface IQueryParserFactory 
{ 
    void Register<TQueryParser>(Func<QueryContext, bool> predicate) 
     where TQueryParser : IQueryParser; 
    IQueryParser Create(QueryContext context); 
} 

public class QueryParserFactory : IQueryParserFactory 
{ 
    private readonly ConcurrentDictionary<Type, Func<QueryContext, bool>> _parsers = new ConcurrentDictionary<Type, Func<QueryContext, bool>>(); 

    public void Register<TQueryParser>(Func<QueryContext, bool> predicate) 
     where TQueryParser : IQueryParser 
     => _parsers.AddOrUpdate(typeof(TQueryParser), predicate, (key, oldValue) => predicate); 

    public IQueryParser Create(QueryContext context) 
     => (from kpv in _parsers 
      where kpv.Value.Invoke(context) 
      select (IQueryParser)Activator.CreateInstance(kpv.Key, context)).FirstOrDefault(); 
} 

パーサは、余分な依存関係を必要とするかもしれないと私は、パーサーにそれらを注入する方法がわからないだけの簡単なバージョンです。次のパーサーがあるとしましょう。IOperatorParserの具体的な実装が必要なのはどうすればわかりますか?

public class QueryParser : IQueryParser 
{ 
    private readonly QueryContext _context; 

    public QueryParser(QueryContext context, IOperatorParser parser) 
    { 
     _context = context; 
    } 

    public Query Parse() 
    { 
     (...) 
    } 
} 

私は何らかの依存性注入が必要です。問題は、オブジェクトを自分でインスタンス化し、実行時にQueryContextも渡すことです。私のユースケースを処理するために私のアプリケーションをどのように再設計することができるかについてのヒント/アイデア?コメントで述べたように

は、 セバスチャン

+1

実際、1つの解決策はすべてのパーサーをインスタンス化して工場に渡すことです。その後、各パーサーはCanParseメソッドを実装する必要があります。理想的ではないが、そのトリックをするだろうか?より良いオプションはありますか? – Seb

+1

私はちょうどランタイム値(https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99)を注入することに関する良い記事を読んだ。これに基づいて、コンストラクタを介してコンテキストの注入を再考することができます。 –

+0

私はあなたの 'CanParse()'ソリューションを以前から使っていました(http://stackoverflow.com/questions/28578105/design-pattern-oraccepted-solutions-for-avoiding-switching-on-types/28580610#28580610) 。それは動作しますが、どこかに良い方法があると確信しています... –

答えて

1

をありがとう、QueryContext contextは、実行時のデータであり、あなたのオブジェクトに注入すべきではありません。

あなたが言及したCanParseソリューションを使用するか、この代替を使用することができます。

public interface IQueryParser 
{ 
    Maybe<Query> Parse(QueryContext context); 
} 

Maybe<T>構造体は、「無価値」の戻り値をサポートするために使用される(これは、より読みやすく、正直です「値なし」の場合はnullQueryが返されます)。これについて詳しくはこちらをご覧ください:http://enterprisecraftsmanship.com/2015/03/13/functional-c-non-nullable-reference-types/

このインターフェイスの実装者は、解析できない場合、「値なし」を返すと予想されます。

は今、消費者の取得は、このような、既に構築されたステートレスパーサのコレクションです:

public class Consumer 
{ 
    private readonly IQueryParser[] parsers; 

    public Consumer(IQueryParser[] parsers) 
    { 
     //.. 
    } 
} 

そして、それが呼び出されるたびに、クエリを解析することができるものが見つかるまで、それはすべてのパーサーをしようとします:

var result = 
    parsers 
     .Select(x => x.Parse(context)) 
     .FirstOrDefault(x => x.HasValue); //the default of the `Maybe` struct is a "no value" 

if(result.HasValue) //some parse was able to parse 
    //... 
else 
    //..