1

可能重复:
一个函数/过程/方法应该有多少行代码?

Out 团队有一个结构不完善的 ansi-c 代码项目。我想使用一些 CC 技术来整理代码库。

至于 C 代码,我们有很多指针和很多 NULL 指针陷阱要捕捉。因此,有很多看起来相似的片段。就像是

if (pointer == NULL)
{
  function1();
  function2();
}

到处都是。然后有很多函数将以相同的方式相互调用,只有一些变化,比如

function1();
function2a();
function3();

function1();
function2b();
function3();

到处都是。

我想将这些块提取为单个函数以减少 LOC 和复制粘贴。但这不仅会创建一个(有点)正交层,还会创建一些功能或多或少相同,除了一些细节。更糟糕的是,它会创建同时做很多事情的函数。

那么,什么是好的策略呢?更重要的是,高层的精益代码、低层的精益功能还是精益架构?哪个原则胜过另一个?关注点分离还是 DRY?

我想重构那个野兽,但不知道从哪里开始。

为了解释下面的示例并输入相同的名称。假设我们有

morningBath();
drinkCoffee();
if (checkMail())
{
  answerMail();
}

并将其放入morningRoutine() 中。现在我们有

drinkTea();
morningBath();
if (checkMail())
{
  answerMail();
}

并称之为 sundayMorningRoutine()。但是有重复的代码。或将 morningRoutine(day) 展开为

if (day == sunday){
  drinkTea();
  morningBath();
} else {
  morningBath();
  drinkCoffee();
}    
if (checkMail())
{
  answerMail();
}

或许

if (day == sunday){
  drink(Tea);
  morningBath();
} else {
  morningBath();
  drink(Coffee);
}    
if (checkMail())
{
  answerMail();
}

我想知道这是不是很好的风格……也许……谢谢你的提示!

4

3 回答 3

1

关于 C 代码,经常遇到NULL指针检查是完全正常的,尤其是在涉及函数参数时。就个人而言,我更喜欢让调用者解决这种情况,例如:

if (p == NULL) {
    /* maybe do some cleanup and then: */
    return errcode;
}

公共函数,即作为 API 一部分的函数,应始终检查NULL指针。指定的功能static可能会在 IMO 放弃这些检查。最后,总是有assert()。编译器标志可以抑制这些检查-NDEBUG。我使用assert()instatic函数而不是if-statements 和“公共”函数进行测试,这些测试表明调用者实际上并不理解整个 API,例如在链表库中:

void list_print(list **l)
{
    assert(l != NULL);    /* no valid list passed by reference can ever be NULL */

    if (*l == NULL)       /* but it can be empty */
        return;

    /* print list */
}


至于您的第二个问题,我可以看到三个选项:

1)让一切保持原样 - 毕竟,它正在工作。

2)引入新功能:

int function_1_2a_3();
int function_1_2b_3();

3) 引入新的参数化函数:

int function_1_2_3(int type);

就个人而言,我更喜欢后一种方法,但这实际上只是风格问题。

于 2012-07-27T12:28:08.907 回答
1

我非常同意 Philip 所说的,但我想补充一点,Clean Code 的要点之一是让代码读起来像英语。如果遇到常见的函数序列,又不能给这个序列起个好听的名字,那么最好还是留下它。例如,如果您有

vacuumTheCarpet();
dustTheFurniture();
putThingsInTherePlace();

你可以用

cleanTheHouse();

但如果你有

getTheMail();
eatSomeIceCream();
writeALetter();

您可能最好将它们作为单独的功能。

于 2012-07-27T12:39:59.913 回答
1

对于错误检查,宏可以使您的代码更清晰:

#define CheckNullLogClean(ptr) if(ptr == NULL) { \
    status = ERR_NULL_PTR; \
    LogError(status); \
    goto cleanup; }

int func(int *input) {
    status = 0;
    CheckNullLogClean(input);
    Do_Things();
    cleanup:
    Release_Resources();
    return status;
}

我曾经使用的代码库做了类似的事情。并且每个函数都被设置为status在一个名为cleanup. 如果每个函数调用都检查了它的返回值,我们的日志文件将包含带有文件名和行号的堆栈跟踪(是一个使用和宏LogError调用函数的宏)。我们可以在整个项目中使用相同的错误检查宏。__FILE____LINE__

哦,如果您的函数用于类似的目的,那么您迭代的函数指针数组可能是有意义的。

于 2012-07-27T13:39:41.077 回答