C# 在不影响业务逻辑的情况下降低圈复杂度
考虑这种方法:C# 在不影响业务逻辑的情况下降低圈复杂度,c#,cyclomatic-complexity,C#,Cyclomatic Complexity,考虑这种方法: public ActionResult DoSomeAction(ViewModel viewModel) { try { if (!CheckCondition1(viewModel)) return Json(new {result = "Can not process"}); if (CheckCondition2(viewModel)) { return Js
public ActionResult DoSomeAction(ViewModel viewModel)
{
try
{
if (!CheckCondition1(viewModel))
return Json(new {result = "Can not process"});
if (CheckCondition2(viewModel))
{
return Json(new { result = false, moreInfo = "Some info" });
}
var studentObject = _helper.GetStudent(viewModel, false);
if (viewModel.ViewType == UpdateType.AllowAll)
{
studentObject = _helper.ReturnstudentObject(viewModel, false);
}
else
{
studentObject.CourseType = ALLOW_ALL;
studentObject.StartDate = DateTime.UtcNow.ToShortDateString();
}
if (studentObject.CourseType == ALLOW_UPDATES)
{
var schedule = GetSchedules();
if (schedule == null || !schedule.Any())
{
return Json(new { result = NO_SCHEDULES });
}
_manager.AddSchedule(schedule);
}
else
{
_manager.AllowAllCourses(studentObject.Id);
}
_manager.Upsert(studentObject);
return Json(new { result = true });
}
catch (Exception e)
{
// logging code
}
}
该方法有许多退出点,圈复杂度为8。
我们的最佳实践表明,它不应大于5
- 是因为多个“如果”吗
- 我可以做些什么来重构它,从而减少退出点
这是我对上述问题的评论摘要
我们的最佳实践表明,它不应大于5 “5”听起来有点低。nDepend和Microsoft分别推荐“”和“” 独立开支: CC高于15的方法很难理解和维护。CC大于30的方法非常复杂,应该分成更小的方法(除非它们是由工具自动生成的) 微软: 圈复杂度=边数-节点数+1 其中,节点表示逻辑分支点,边表示节点之间的线。 当圈复杂度大于25时,该规则报告一个冲突 作品: “是不是因为多个如果”- 是的,还有
|
我可以做些什么来重构它,从而减少退出点
一个简单的方法是只使用方法和“”。i、 e.将部分if
逻辑移到一个或多个方法中,每个方法调用另一个方法,而不是一个方法中的所有if
s
e、 g
可以通过将最内部的if
组移动到单独的方法中来改进:
void Foo(object person)
{
if (...)
{
// ...
}
else if (...)
{
// ...
}
if (x == 1 && person is Hobbit)
{
DoHobbitStuff();
}
}
void DoHobbitStuff()
{
if (...)
{
// ...
}
if (...)
{
// ...
}
if (...)
{
// ...
}
}
然而,在“5”中,我不认为您的代码需要重构来减少CC
我可以做些什么来重构它,从而减少退出点
根据,退出点(如return
)不计算在内:
以下表达式不计入CC计算:
否则| do | switch | try |使用|抛出|最终|返回|对象创建|方法调用|字段访问
看看你的代码,很明显,你的高圈复杂度和难以重构的方式是糟糕设计的标志(例如代码气味)。让我们复习一下 这些东西是什么?为什么他们的名字这么模棱两可?如果你找不到其他合适的名字,那就意味着你对关注点的分离是错误的 我甚至无法想象这些方法是如何起作用的。一些通用助手如何知道如何从通用ViewModel获取“学生”?这两种方法的区别是什么 这看起来好像是ViewModel的一部分。您正在根据ViewModel的内部状态做出决策,这是只有ViewModel才允许做的事情 那是“UpdateOrInsert”吗?这是一种奇怪的命名惯例
另一件让我困惑的事情是,您似乎在使用类似MVC的web调用实现,但您使用的是ViewModels。这到底是怎么回事?我总是将ViewModels与UI绑定,而不是与web绑定。“5”听起来有点低。NDepende和Microsoft建议并重新激活。“这是因为多个如果”-是的,还有
|
。我同意这一点。从学习的角度来看,是否可以重构此代码以减少IFs?当然可以。一个简单的方法就是采用这种方法,尽管你提出了一些好的观点,但我不完全确定这个答案与圈复杂度有什么关系。我同意@MickyDuncan,这里肯定有好的观点,但与圈复杂度无关。另外,Upsert
是更新或插入的一个非常常见的名称,我看不出这有什么错(除了有一个名为\u manager
的变量有这个方法,但方法名称本身没有)。
void Foo(object person)
{
if (...)
{
// ...
}
else if (...)
{
// ...
}
if (x == 1 && person is Hobbit)
{
DoHobbitStuff();
}
}
void DoHobbitStuff()
{
if (...)
{
// ...
}
if (...)
{
// ...
}
if (...)
{
// ...
}
}
_helper
_manager
_helper.GetStudent(viewModel, false);
_helper.ReturnstudentObject(viewModel, false);
var studentObject = _helper.GetStudent(viewModel, false);
if (viewModel.ViewType == UpdateType.AllowAll)
{
studentObject = _helper.ReturnstudentObject(viewModel, false);
}
else
{
studentObject.CourseType = ALLOW_ALL;
studentObject.StartDate = DateTime.UtcNow.ToShortDateString();
}
_manager.Upsert(studentObject);