c语言 - DRY 如何与"Separation of concern"和"One function, one task"相结合?



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

我们的团队有一个结构不好的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();
}

我不知道这是不是好风格。。大概谢谢你的提示!

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

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

公共函数,即作为API一部分的函数,应始终检查NULL指针。被指定为static的功能可以IMO放弃那些检查。最后,总有assert()。可以通过编译器标志-NDEBUG来抑制这些检查。我在static函数中使用assert(),而不是if-语句,在"公共"函数中使用了CCD_6进行测试,这些测试表明调用方实际上并不理解API的整体,例如在链表lib:中

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

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

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

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

你可以用代替它

cleanTheHouse();

但是如果你有

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

你最好把它们作为单独的函数。

对于错误检查,宏可以使代码更加干净:

#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;
}

我以前工作的一个代码库也做了类似的事情。每个函数都被设置为在名为cleanup的标签之后返回一个名为CCD-9的整数(在模块范围的错误代码表中有一个值)。如果每个函数调用都检查了返回值,那么我们的日志文件将包含带有文件名和行号的堆栈跟踪(LogError是一个宏,它使用__FILE____LINE__宏调用函数)。我们可以在整个项目中使用相同的错误检查宏。

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

相关内容

最新更新