C# 查找(并提取)复杂关联以查找规则冲突
我正在编写一些写得不是很好的代码,并且涉及一些我希望重构的相当复杂的逻辑。本主题是验证规则和报告潜在违规行为。不幸的是,这个类的设计很奇怪,所以我遇到了一些难以计数的挑战 作为一个简化的例子,我有以下几点:C# 查找(并提取)复杂关联以查找规则冲突,c#,linq,ienumerable,matching,rules,C#,Linq,Ienumerable,Matching,Rules,我正在编写一些写得不是很好的代码,并且涉及一些我希望重构的相当复杂的逻辑。本主题是验证规则和报告潜在违规行为。不幸的是,这个类的设计很奇怪,所以我遇到了一些难以计数的挑战 作为一个简化的例子,我有以下几点: IEnumerable<RuleDefinition> IEnumerable<Request> 显然,当请求类型匹配并且两个请求之间的间隔(TimeIndex)太短时,就违反了该规则。现在,我想摘录: 如果存在违反规则的情况(这相当容易) 违反了哪些规则 哪些请
IEnumerable<RuleDefinition>
IEnumerable<Request>
显然,当请求类型匹配并且两个请求之间的间隔(TimeIndex)太短时,就违反了该规则。现在,我想摘录:
- 如果存在违反规则的情况(这相当容易)
- 违反了哪些规则
- 哪些请求违反了规则
public class Violation
{
public RuleDefinition ViolatedRule { get; set; }
public Request FirstRequest { get; set; }
public Request SecondRequest { get; set; }
}
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
List<Request> requestList = requests.ToList();
return ruleDefinitions.SelectMany(rule => FindViolationsInRequests(requestList, rule));
}
private IEnumerable<Violation> FindViolationsInRequests(IEnumerable<Request> allRequest, RuleDefinition rule)
{
return FindMatchingRequests(allRequest, rule)
.Select(firstRequest => FindSingleViolation(allRequest, firstRequest, rule))
.Where(violation => violation != null);
}
private Violation FindSingleViolation(IEnumerable<Request> allRequest, Request request, RuleDefinition rule)
{
Request collidingRequest = FindCollidingRequest(allRequest, request, rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
return new Violation
{
ViolatedRule = rule,
FirstRequest = request,
SecondRequest = collidingRequest
};
}
return null;
}
private IEnumerable<Request> FindMatchingRequests(IEnumerable<Request> requests, RuleDefinition rule)
{
return requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
}
private Request FindCollidingRequest(IEnumerable<Request> requests, Request firstRequest, int minimumDistanceBetweenRequests)
{
return requests.FirstOrDefault(secondRequest => IsCollidingRequest(firstRequest, secondRequest, minimumDistanceBetweenRequests));
}
private bool IsCollidingRequest(Request firstRequest, Request secondRequest, int minimumDistanceBetweenRequests)
{
return secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < minimumDistanceBetweenRequests;
}
}
我认为这是一个相当简单的问题,但我没能找到一个可读性和可维护性都很好的解决方案。我试过各种各样的东西。。结果总是乱七八糟的(我刚刚尝试实现这个示例,结果很糟糕)
在这种情况下有什么想法和模式可以使用吗?
(Resharper经常正确地建议。选择Many,但这会使内容更不可读)
编辑:这是我漫长而丑陋的实现
var ruleDefinitions=新列表
{
新规则定义{
ConcerningRequestType=RequestType.Exclusive,
请求之间的最小距离=2}
};
var请求=新列表()
{
新请求{TimeIndex=1,TypeOfThisRequest=RequestType.Normal},
新请求{TimeIndex=1,TypeOfThisRequest=RequestType.Normal},
新请求{TimeIndex=2,TypeOfThisRequest=RequestType.Normal},
新请求{TimeIndex=3,TypeOfThisRequest=RequestType.Exclusive},
新请求{TimeIndex=4,TypeOfThisRequest=RequestType.Exclusive},
};
var违规=新列表();
foreach(规则定义中的var规则)
{
var requestsMatchingType=requests.Where(r=>r.TypeOfThisRequest==rule.ConcerningRequestType);
foreach(requestsMatchingType中的var firstRequest)
{
var collidingRequest=requests.FirstOrDefault(secondRequest=>
secondRequest.TimeIndex>firstRequest.TimeIndex&&
Math.Abs(secondRequest.TimeIndex-firstRequest.TimeIndex)
这不是一项简单的任务,因此我要做的第一件事是定义一个接口,以查看我在这里需要什么:
interface IViolationFinder
{
IEnumerable<Violation> Search(
IEnumerable<RuleDefinition> ruleDefinitions,
IEnumerable<Request> requests);
}
接口IViolationFinder
{
IEnumerable搜索(
IEnumerable规则定义,
i无数请求);
}
现在我们清楚地看到我们需要实施什么。因为你的搜索逻辑非常复杂,我认为你不应该用一个linq来表达它。你可以,但你不应该。嵌入linq的两个嵌套foreach循环非常糟糕,我认为使用linq本身不会更干净
您需要的是在实现中创建更多的方法。这将增加可读性。因此,天真的实现是这样的(这是你的):
类ViolationFinder:IViolationFinder
{
公共IEnumerable搜索(IEnumerable规则定义、IEnumerable请求)
{
var违规=新列表();
foreach(规则定义中的var规则)
{
var requestsMatchingType=requests.Where(r=>r.TypeOfThisRequest==rule.ConcerningRequestType);
foreach(requestsMatchingType中的var firstRequest)
{
var collidingRequest=requests.FirstOrDefault(secondRequest=>
secondRequest.TimeIndex>firstRequest.TimeIndex&&
Math.Abs(secondRequest.TimeIndex-firstRequest.TimeIndex)
你可以开始重构这个。与其用一种方法思考,不如让我们提取最明显的部分:
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
var violations = new List<Violation>();
foreach (RuleDefinition rule in ruleDefinitions)
{
IEnumerable<Request> requestsMatchingType = requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
violations.AddRange(
FindViolationsInRequests(requestsMatchingType, requests, rule));
}
return violations;
}
private IEnumerable<Violation> FindViolationsInRequests(
IEnumerable<Request> matchingRequests,
IEnumerable<Request> allRequest,
RuleDefinition rule)
{
foreach (Request firstRequest in matchingRequests)
{
var collidingRequest = allRequest.FirstOrDefault(secondRequest =>
secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
yield return new Violation
{
ViolatedRule = rule,
FirstRequest = firstRequest,
SecondRequest = collidingRequest
};
}
}
}
}
类ViolationFinder:IViolationFinder
{
公共IEnumerable搜索(IEnumerable规则定义、IEnumerable请求)
{
var违规=新列表();
foreach(规则定义中的规则定义规则)
{
IEnumerable requestsMatchingType=requests.Where(r=>r.TypeOfThisRequest==rule.ConcerningRequestType);
AddRange.AddRange(
FindViolationsRequests(requestsMatchingType、requests、rule));
}
返回违规行为;
}
私有IEnumerable FindViolationsInRequests(
IEnumerable匹配请求,
IEnumerable allRequest,
规则(定义规则)
{
foreach(matchingRequests中的Request firstRequest)
{
var collingrequest=allRequest.FirstOrDefault(secondRequest=>
secondRequest.TimeIndex>firstRequest.TimeIndex&&
Math.Abs(secondRequest.TimeIndex-firstRequest.TimeIndex)
搜索几乎是清白的,但我们是清白的
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
var violations = new List<Violation>();
foreach (var rule in ruleDefinitions)
{
var requestsMatchingType = requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
foreach (var firstRequest in requestsMatchingType)
{
var collidingRequest = requests.FirstOrDefault(secondRequest =>
secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
violations.Add(new Violation
{
ViolatedRule = rule,
FirstRequest = firstRequest,
SecondRequest = collidingRequest
});
}
}
}
return violations;
}
}
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
var violations = new List<Violation>();
foreach (RuleDefinition rule in ruleDefinitions)
{
IEnumerable<Request> requestsMatchingType = requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
violations.AddRange(
FindViolationsInRequests(requestsMatchingType, requests, rule));
}
return violations;
}
private IEnumerable<Violation> FindViolationsInRequests(
IEnumerable<Request> matchingRequests,
IEnumerable<Request> allRequest,
RuleDefinition rule)
{
foreach (Request firstRequest in matchingRequests)
{
var collidingRequest = allRequest.FirstOrDefault(secondRequest =>
secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
yield return new Violation
{
ViolatedRule = rule,
FirstRequest = firstRequest,
SecondRequest = collidingRequest
};
}
}
}
}
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
var violations = new List<Violation>();
foreach (RuleDefinition rule in ruleDefinitions)
{
violations.AddRange(FindViolationsInRequests(requests, rule));
}
return violations;
}
private IEnumerable<Violation> FindViolationsInRequests(
IEnumerable<Request> allRequest,
RuleDefinition rule)
{
foreach (Request firstRequest in FindMatchingRequests(allRequest, rule))
{
var collidingRequest = allRequest.FirstOrDefault(secondRequest =>
secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
yield return new Violation
{
ViolatedRule = rule,
FirstRequest = firstRequest,
SecondRequest = collidingRequest
};
}
}
}
private IEnumerable<Request> FindMatchingRequests(IEnumerable<Request> requests, RuleDefinition rule)
{
return requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
}
}
var collidingRequest = allRequest.FirstOrDefault(secondRequest =>
secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < rule.MinimumDistanceBetweenRequests);
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
var violations = new List<Violation>();
foreach (RuleDefinition rule in ruleDefinitions)
{
violations.AddRange(FindViolationsInRequests(requests, rule));
}
return violations;
}
private IEnumerable<Violation> FindViolationsInRequests(
IEnumerable<Request> allRequest,
RuleDefinition rule)
{
foreach (Request firstRequest in FindMatchingRequests(allRequest, rule))
{
Request collidingRequest = FindCollidingRequest(allRequest, firstRequest, rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
yield return new Violation
{
ViolatedRule = rule,
FirstRequest = firstRequest,
SecondRequest = collidingRequest
};
}
}
}
private IEnumerable<Request> FindMatchingRequests(IEnumerable<Request> requests, RuleDefinition rule)
{
return requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
}
private Request FindCollidingRequest(IEnumerable<Request> requests, Request firstRequest, int minimumDistanceBetweenRequests)
{
return requests.FirstOrDefault(secondRequest => IsCollidingRequest(firstRequest, secondRequest, minimumDistanceBetweenRequests));
}
private bool IsCollidingRequest(Request firstRequest, Request secondRequest, int minimumDistanceBetweenRequests)
{
return secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < minimumDistanceBetweenRequests;
}
}
class ViolationFinder : IViolationFinder
{
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
List<Request> requestList = requests.ToList();
return ruleDefinitions.SelectMany(rule => FindViolationsInRequests(requestList, rule));
}
private IEnumerable<Violation> FindViolationsInRequests(IEnumerable<Request> allRequest, RuleDefinition rule)
{
return FindMatchingRequests(allRequest, rule)
.Select(firstRequest => FindSingleViolation(allRequest, firstRequest, rule))
.Where(violation => violation != null);
}
private Violation FindSingleViolation(IEnumerable<Request> allRequest, Request request, RuleDefinition rule)
{
Request collidingRequest = FindCollidingRequest(allRequest, request, rule.MinimumDistanceBetweenRequests);
if (collidingRequest != null)
{
return new Violation
{
ViolatedRule = rule,
FirstRequest = request,
SecondRequest = collidingRequest
};
}
return null;
}
private IEnumerable<Request> FindMatchingRequests(IEnumerable<Request> requests, RuleDefinition rule)
{
return requests.Where(r => r.TypeOfThisRequest == rule.ConcerningRequestType);
}
private Request FindCollidingRequest(IEnumerable<Request> requests, Request firstRequest, int minimumDistanceBetweenRequests)
{
return requests.FirstOrDefault(secondRequest => IsCollidingRequest(firstRequest, secondRequest, minimumDistanceBetweenRequests));
}
private bool IsCollidingRequest(Request firstRequest, Request secondRequest, int minimumDistanceBetweenRequests)
{
return secondRequest.TimeIndex > firstRequest.TimeIndex &&
Math.Abs(secondRequest.TimeIndex - firstRequest.TimeIndex) < minimumDistanceBetweenRequests;
}
}
public IEnumerable<Violation> Search(IEnumerable<RuleDefinition> ruleDefinitions, IEnumerable<Request> requests)
{
List<Request> requestList = requests.ToList();
return ruleDefinitions.SelectMany(rule => FindViolationsInRequests(requestList, rule));
}
var violations = from rule in ruleDefinitions
join r1 in requests on rule.ConcerningRequestType equals r1.TypeOfThisRequest
join r2 in requests on rule.ConcerningRequestType equals r2.TypeOfThisRequest
where r1 != r2 &&
r2.TimeIndex > r1.TimeIndex &&
Math.Abs(r2.TimeIndex - r1.TimeIndex) < rule.MinimumDistanceBetweenRequests
select new Violation() { FirstRequest = r1, SecondRequest = r2, ViolatedRule = rule };