DRY如何与“关注点分离”和“一个功能,一个任务”相结合?

时间:2012-07-27 11:23:07

标签: c refactoring dry code-cleanup

  

可能重复:
  How many lines of code should a function/procedure/method have?

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

对于C代码,我们有很多指针和很多NULL指针陷阱。因此,有许多片段看起来很相似。就像

if (pointer == NULL)
{
  function1();
  function2();
}
到处都是。然后有很多函数会以相同的方式相互调用,只有几个变体,比如

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

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

到处都是。

我想将这些块作为单个函数提取,以减少LOC和复制粘贴。但是,除了一些细节之外,这不仅会产生(某种程度上)正交层,而且会产生或多或少相同的功能。更糟糕的是,它会创建能够同时执行许多操作的功能。

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

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

利用下面的示例并将相同的名称放入。让我们假设我们有

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

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

2 个答案:

答案 0 :(得分:1)

关于C代码,经常遇到NULL指针检查是完全正常的,特别是在涉及函数参数时。就个人而言,我更愿意让来电者解决问题,如:

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

公共函数,即属于API的函数,应始终检查NULL指针。指定为static的函数可以通过IMO删除这些检查。最后,始终有assert()。编译器标志-NDEBUG可以抑制这些检查。我在assert()函数中使用static而不是if - 语句和“公共”函数中的测试,这些函数显示调用者实际上并不理解整个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);

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

答案 1 :(得分: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__宏的函数的宏)。我们可以在整个项目中使用相同的错误检查宏。

哦,如果你的函数用于类似的目的,也许你迭代的函数指针数组是有意义的。