C# - 用于迭代谓词的模式

时间:2016-12-23 10:39:24

标签: c# linq design-patterns iteration

我做了一个我不喜欢的模式。

如下:

List<Element> listOfPossibleResults = getAllPossibleResults();

Element result = findResult(getFirstPriorityElements(listOfPossibleResults));
if (result!= null)
{
 return result;
}

result = findResult(getSecondPriorityElements(listOfPossibleResults));
if (result!= null)
{
 return result;
}

private Element findResult(List<Element> elements) {...};
private List<Element> getFirstPriorityElements(List<Element> elements) {...};
private List<Element> getSecondPriorityElements(List<Element> elements) {...};

等。

基本上我正在根据一些规则创建子列表。创建子列表后,我尝试在其中查找特定元素。如果我没找到,我继续下一个优先级,依此类推。

我想要一个解决方案,我可以迭代这些标准,直到找到解决方案。但我不知道如何让他们采用我可以迭代的格式。

你们可以给我一个C#特定的问题解决方案吗?

6 个答案:

答案 0 :(得分:2)

正如@Lepijohnny所提到的,您可以使用Chain of responsibility设计模式。例如:

abstract class Handler<TRequest, TResult>
{
  protected Handler<TRequest, TResult> successor;

  public void SetSuccessor(Handler<TRequest, TResult> successor)
  {
    this.successor = successor;
  }

  public abstract TResult HandleRequest(TRequest request);
}

class FirstHandler : Handler<List<Element>, Element>
{
  public override void HandleRequest(TRequest request)
  {
    Element result = findResult(getFirstPriorityElements(request));
    if (result == null)
    {
      result = sucessor?.HandleRequest(request);
    }
    return result;
  }

  private Element findResult(List<Element> elements) {...};
  private List<Element> getFirstPriorityElements(List<Element> elements) {...};
}

class SecondHandler : Handler<List<Element>, Element>
{
  public override void HandleRequest(TRequest request)
  {
    Element result = findResult(getSecondPriorityElements(request));
    if (result == null)
    {
      result = sucessor?.HandleRequest(request);
    }
    return result;
  }

  private Element findResult(List<Element> elements) {...};
  private List<Element> getSecondPriorityElements(List<Element> elements) {...};
}

用法:

void Example()
{
  // Setup Chain of Responsibility
  var h1 = new FirstHandler();
  var h2 = new SecondHandler();
  h1.SetSuccessor(h2);

  var result = h1.Handle(new List<Element>());
}

这是一个很快的例子。我认为它描述了这种模式是如何工作的,你可以根据自己的需要进行调整。

答案 1 :(得分:1)

在“result”类中放入一个名为“Priority(int)”的属性,然后:

result = listOfPossibleResults.GroupBy(x => x.Priority).OrderBy(x => x.Key);

然后:

return result.FirstOrDefault(x => x.Count() > 0);

首次检索时,您需要填写结果项的优先级。

P.S。我在这里输入代码,如果某处有拼写错误,请原谅我。

答案 2 :(得分:1)

如果您可以将方法getFirstPriorityElements(List&lt;&gt; list)重构为单个getPriorityElements(List&lt;&gt; list,int nr),您可以执行以下操作

method IteratePredicates(List<> list, int nr = 0) 
{
    if (nr>maxpriority) return null;
    return findresult(getPriorityElements(list,nr)) ?? IteratePredicates(list,nr++);
}

在for循环中:

    method IteratePredicates(List<> list, int nr = 0)
    {
        for (int i = 0; i < maxpriority; i++)
        {
            var result = findresult(getPriorityElements(list, nr));
            if (result != null)
                return result;
        }
        return null;
    }

答案 3 :(得分:1)

您可以使用Func<T, T>将方法视为对象,然后您也可以将它们放在例如var methods = new Func<List<Element>, List<Element>>[] { getFirstPriorityElements, getSecondPriorityElements }; return methods .Select(method => findResult(method(listOfPossibleResults))) .Where(result => result != null) .FirstOrDefault(); 中。数组。然后你可以迭代数组,逐个调用方法,直到找到结果。

然后解决方案变为:

{{1}}

这简短易读,无需更改方法或类型即可运行,无需为了应用模式而添加类。

答案 4 :(得分:1)

我是对的,你的get__PriorityElements实际上是一个过滤器吗?在这种情况下,它更具说明性,并且希望更具可读性来对待这样的人:

Func<Element, bool> isFirstPriority = ...;
var firstPriorityElements = elements.Where(isFirstPriority);

现在你的总体目标是使用findResult中包含的谓词从最高优先级子序列中提取单个元素(或不提取)?所以用实际谓词替换它

Func<Element, bool> isResult = ...;
像这样。现在你要查看isResult匹配的所有第一优先级元素,然后如果没有找到所有第二优先级元素等等。这听起来就像一个序列连接!所以我们最终得到了

var prioritisedSequence = elements
    .Where(isFirstPriority)
    .Concat(elements
        .Where(isSecondPriority))
    .Concat....;

最后结果

var result = prioritisedSequence
    .FirstOrDefault(isResult);

由于WhereConcat被懒惰地枚举,因此它具有声明性,同时避免了超出必要的工作量,并且它也是轻量级的,并且也是“LINQy”。

如果你想要抽象它更多,并预测优先级安排的变化,你实际上可以为这样的人创建一个更高的顺序列表:

IEnumerable<Func<Element, bool>> priorityFilters = new[]
{
    isFirstPriority,
    isSecondPriority,
    ...
};

然后可以将串联作为该序列的聚合来执行:

var prioritisedSequence = priorityFilters
    .Aggregate(
        Enumerable.Empty<Element>(),
        (current, filter) => current.Concat(elements.Where(filter)));

此更改可能会使将来更容易添加新的优先级,或者您可能认为它会使代码的意图变得混乱和隐藏。

答案 5 :(得分:0)

您可以使用规格模式
这是一个示例代码:
使用条件创建界面:

public class GlobalSongSpecification : ISpecification<Song>
{
    public List<int> GenreIdsToInclude { get; set; } = new List<int>();
    public List<int> AlbumIdsToInclude { get; set; } = new List<int>();
    public List<string> ArtistsToInclude { get; set; } = new List<string>();
    public string TitleFilter { get; set; }
    public int MinRating { get; set; }

    [JsonIgnore]
    public Expression<Func<Song, bool>> Criteria
    {
        get
        {
            return s =>
                (!GenreIdsToInclude.Any() || s.Genres.Any(g => GenreIdsToInclude.Any(gId => gId == g.Id))) &&
                (!AlbumIdsToInclude.Any() || AlbumIdsToInclude.Contains(s.AlbumId)) &&
                (!ArtistsToInclude.Any() ||ArtistsToInclude.Contains(s.Artist)) &&
                (String.IsNullOrEmpty(this.TitleFilter) || s.Title.Contains(TitleFilter)) &&
                s.Rating >= MinRating;
        }
    }
}

然后创建一个包含查询规范的类:

 public interface ISongRepository
{
    IEnumerable<Song> List(ISpecification<Song> specification);
    //IQueryable<Song> List();
    Song GetById(int id);
    void Add(Song song);
    IEnumerable<string> AllArtists();
    IEnumerable<Genre> AllGenres();
}

创建一个存储库,其中包含一个公开接收ISpecification的方法:

public ActionResult Index(List<int> selectedGenres = null, 
        List<string> selectedArtists = null, 
        string titleSearch = null,
        int minRating = 0,
        string filter = null,
        string save = null,
        string playlistName = null)
    {
        if (selectedArtists == null) { selectedArtists = new List<string>(); }
        if (selectedGenres == null) { selectedGenres = new List<int>(); }

        var spec = new GlobalSongSpecification();
        spec.ArtistsToInclude.AddRange(selectedArtists);
        spec.GenreIdsToInclude.AddRange(selectedGenres);
        spec.MinRating = minRating;
        spec.TitleFilter = titleSearch;

        var songs = _songRepository.List(spec);

        //You can work with the filtered data at this point
    }

您的客户端代码调用GlobalSongSpecification,填充它并将其传递到存储库,以便按标准进行筛选:

<!DOCTYPE HTML>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<script>
document.write(document.compatMode); // CSS1Compat
</script>
</html>

然后填充剃刀视图或将其作为web api公开。 示例代码来自pluralsight desing模式库课程Here(Specification Pattern module)