2016-12-07 73 views
0

我找不到解决我所面临的设计问题的好方案。目前我正在设计一个系统应该解析查询字符串。通用/继承设计问题

每个解析器必须实现的接口IQueryParser:

public interface IQueryParser 
{ 
    Query Parse(); 
} 

当我收到一个HTTP请求时,我试图找到能够使用工厂来分析它的分析器。 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

实际上,一种解决方案可能是实例化所有解析器并将它们传递给工厂。然后每个解析器都需要实现一个CanParse方法。不理想,但会做的伎俩?有更好的选择吗? – Seb

+1

我刚刚阅读了一篇关于注入运行时值的好文章(https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99)。基于此,您可能需要重新考虑通过构造函数注入上下文。 –

+0

我之前使用过'CanParse()'解决方案(http://stackoverflow.com/questions/28578105/design-pattern-or-accepted-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 
    //..