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);