Clean coding: how to break loops from inside a function call inside a loop
干净的编码原则通常包括一个规则,即函数必须是小的和单用途的。
从罗伯特·马丁的书《干净的代码》中可以看出:"函数的第一条规则是它们应该是小的。函数的第二个规则是它们应该小于这个值。"
如果我有一个具有复杂循环的函数,其中包含可以中断循环的分支,那么很难坚持这一点。例如,国际象棋变种中的这段代码是为了让一个单位跳过棋盘上的另一个单位进行攻击:
1 2 3 4 5 6 7 8 9 10 11 12 | for (arow = row, acol = col - 1, found_obstacle = false; acol >= 0; --acol) { if (!found_obstacle) { if (cellHasUnit(arow, acol)) found_obstacle = true; continue; } else { if (cellHasUnit(arow, acol)) { if (!cellHasAlly(arow, acol)) _dangerzones.insert(std::make_pair(arow, acol)); break; } } } |
我理解您不能从函数内部中断一个循环,在该循环中调用函数。
为了用短函数维护干净的代码,处理循环内复杂的中断条件的好方法是什么?我可以想象使用带有返回值的特殊函数来指示是否需要中断,但这仍然意味着每个中断都需要自己的函数。如果我有很多中断条件,这意味着包含主循环的函数仍然很大。
编辑:我在问一个一般性的问题,即(从干净的编码角度)在具有许多中断条件的循环代码中进行模块化是否可行和可取。
作为一个拥有15年以上编程经验的程序员,我可以先告诉你,你提供的报价非常好,你应该遵循它来制作模块化代码,但这并不意味着每个函数都应该是10行代码。那是不可能的。
关于您的代码,这是可以的。不复杂。您在循环中使用函数,它看起来像模块化的。休息也可以。
不过,我有一点意见,使用
1 2 3 4 | if (cellHasUnit(arow, acol)) { found_obstacle = true; else { ... |
有些人完全不鼓励
也许在
你可能会发现这种形式更有表现力。它颠倒了测试,因此避免了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <map> std::map<int, int> _dangerzones; bool cellHasUnit(int, int); bool cellHasAlly(int, int); void handleAlly(int arow, int acol) { if (!cellHasAlly(arow, acol)) _dangerzones.insert(std::make_pair(arow, acol)); } void test(int row, int col) { int arow = row; bool found_obstacle = false; for (int acol = col - 1 ; acol >= 0 ; --acol) { if (cellHasUnit(arow, acol)) { if (found_obstacle) { return handleAlly(arow, acol); } else // not strictly necessary found_obstacle = true; } } } |
RETURN语句用于指示在本例中,循环的中断也必然是测试函数的结束。
如果实际函数较长,则可以编写此函数:
1 2 3 4 5 6 | if (found_obstacle) { handleAlly(arow, acol); break; } else ... |