可能重复:
一个函数/过程/方法应该有多少行代码?
我们的团队有一个结构不好的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__
宏调用函数)。我们可以在整个项目中使用相同的错误检查宏。
哦,如果你的函数也有类似的用途,也许你迭代的函数指针数组是有意义的。