前几天我在代码审查中遇到了一些看起来像这样的代码。
public void DoSomeTasks()
{
if (CheckSomeState()==true) return;
DoTaskOne();
if (CheckSomeState()==true) return;
DoTaskTwo();
if (CheckSomeState()==true) return;
DoTaskThree();
if (CheckSomeState()==true) return;
DoTaskFour();
}
随着任务数量的增加,代码最终会出现更高的圈复杂度,而且对我来说也感觉不对。
我提出解决此问题的解决方案是。
private void DoTasksWhile(Func<bool> condition, Action[] tasks)
{
foreach (var task in tasks)
{
if (condition.Invoke()==false) break;
task.Invoke();
}
}
像这样使用
public void DoSomeTasks()
{
var tasks = new Action[] {
{()=DoTaskOne()},
{()=DoTaskTwo()},
{()=DoTaskThree()},
{()=DoTaskFour()}
}
DoTasksWhile(()=>CheckSomeState(), tasks);
}
任何人都有任何建议让代码更具可读性吗?
答案 0 :(得分:4)
我对您的实现进行了一些重构
private void DoTasksWhile(Func<bool> predicate, IEnumerable<Action> tasks)
{
foreach (var task in tasks)
{
if (!predicate())
return;
task();
}
}
Invoke
名代表。只需执行它们true/false
进行比较(它没用,你可以错误地指定布尔值)IEnumerable
适用于参数您也可以创建扩展方法
public static void DoWhile(this IEnumerable<Action> actions,Func<bool> predicate)
{
foreach (var action in actions)
{
if (!predicate())
return;
actions();
}
}
用法非常简单:
tasks.DoWhile(() => CheckSomeState());
答案 1 :(得分:1)
如果您的业务流程代码非常复杂,请考虑使用WF(工作流基础)等框架。
否则,您的代码很好,但我不会更改初始代码,因为它更具可读性。它也更灵活,因为将来您可以修改这些IF,即条件可能不会保持相同。
我没有看到圈降复杂性如何降低或降低。
答案 2 :(得分:0)
你的解决方案是完全足够的(尽管在四个步骤的情况下可能有点过分),但在这种情况下它是否是最好的方法取决于CheckSomeState在你的程序环境中的实际目的是什么:
因此,例如,如果CheckSomeState实际上正在检查取消标志,那么在长时间运行的任务中可能需要额外的检查。
您可以使用的另一个选项是抛出异常(例如OperationCancelledException)。这可以使您的圈复杂度非常低,您现在可以做到:
CheckForCancel();
DoTaskOne();
CheckForCancel();
DoTaskTwo();
(缺点是有些人会认为这是使用控制流的异常,这通常是不赞成的。)
我还应该补充一点,目标不应该是减少圈复杂度,而是要使代码易于理解和维护。循环复杂性是一个有用的指标,但它有时会不必要地惩罚代码非常简单。