我做了一个我不喜欢的模式。
如下:
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#特定的问题解决方案吗?
答案 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);
由于Where
和Concat
被懒惰地枚举,因此它具有声明性,同时避免了超出必要的工作量,并且它也是轻量级的,并且也是“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)