考虑这种方法:
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 的Cyclomatic Complexity。 我们的最佳做法是说它不应该大于 5 。
是因为有多个IF吗?
我可以做些什么来重构这个,以便它有更少的退出点?
提前致谢。
答案 0 :(得分:1)
这是我在上述问题下的评论摘要
我们的最佳实践表明它不应该大于5。
“5”听起来有点低。 nDepend和Microsoft分别推荐“30”和“25”。
NDepend的:
CC高于15的方法难以理解和维护。 CC高于30的方法非常复杂,应分成较小的方法(除非它们是由工具自动生成的)
微软:
圈复杂度=边数 - 节点数+ 1 其中节点表示逻辑分支点,边表示节点之间的线。 当圈复杂度超过25时,规则会报告违规行为。
OP:
“是因为多个IF” -
是和||
我可以做些什么来重构这个,以便它有更少的退出点?
一种简单的方法就是采用方法和“split into smaller methods”。即,而不是在一种方法中的所有if
s,将一些if
逻辑移动到一个或多个方法中,每个方法都调用另一个。
e.g。
class Class1
{
class Hobbit
{
}
void Foo(object person)
{
if (...)
{
// ...
}
else if (...)
{
// ...
}
if (x == 1 && person is Hobbit)
{
if (...)
{
// ...
}
if (...)
{
// ...
}
if (...)
{
// ...
}
}
}
}
可以通过将最里面的if
组移动到单独的方法中来改进:
void Foo(object person)
{
if (...)
{
// ...
}
else if (...)
{
// ...
}
if (x == 1 && person is Hobbit)
{
DoHobbitStuff();
}
}
void DoHobbitStuff()
{
if (...)
{
// ...
}
if (...)
{
// ...
}
if (...)
{
// ...
}
}
然而,在“5”时,我不相信你的代码需要重构以达到减少CC的目的。
我可以做些什么来重构这个,以便它有更少的退出点?
根据nDepend,return
等退出点不计算在内:
以下表达式不计入CC计算:
其他|做|开关|试试|使用|扔|终于|返回|对象创建|方法调用|现场访问
答案 1 :(得分:0)
看一下你的代码,很明显你的高圈复杂度和难以重构的方法都是糟糕设计的指标(例如代码味道)。让我们回顾一下。
_helper _manager
这些东西是什么?为什么他们有这么模糊的名字?如果你找不到任何其他合适的名称,那就意味着你的担忧分离是错误的。
_helper.GetStudent(viewModel, false); _helper.ReturnstudentObject(viewModel, false);
我甚至无法想象这些方法如何运作。一些通用帮助器如何知道如何从通用ViewModel获取“学生”?这两种方法有什么区别?
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(); }
这看起来好像应该是ViewModel的一部分。您正在根据ViewModel的内部状态做出决策,只允许ViewModel执行此操作。
_manager.Upsert(studentObject);
那是“UpdateOrInsert”吗?这是一些奇怪的命名惯例。
让我感到困惑的另一件事是你似乎在使用类似MVC的Web调用实现,但是你正在使用ViewModels。这怎么样?我总是将ViewModels与UI绑在一起,而不是与web绑定。