这是我长时间查看这个奇妙的网页后的第一个问题。
可能我的问题有点傻,但我想知道别人对此的看法。更好的是,创建几种特定方法,或者另一方面,只有一种通用方法?这是一个例子......
unsigned char *Method1(CommandTypeEnum command, ParamsCommand1Struct *params)
{
if(params == NULL) return NULL;
// Construct a string (command) with those specific params (params->element1, ...)
return buffer; // buffer is a member of the class
}
unsigned char *Method2(CommandTypeEnum command, ParamsCommand2Struct *params)
{
...
}
unsigned char *Method3(CommandTypeEnum command, ParamsCommand3Struct *params)
{
...
}
unsigned char *Method4(CommandTypeEnum command, ParamsCommand4Struct *params)
{
...
}
或
unsigned char *Method(CommandTypeEnum command, void *params)
{
switch(command)
{
case CMD_1:
{
if(params == NULL) return NULL;
ParamsCommand1Struct *value = (ParamsCommand1Struct *) params;
// Construct a string (command) with those specific params (params->element1, ...)
return buffer;
}
break;
// ...
default:
break;
}
}
我不喜欢后一个选项的主要是这个,
ParamsCommand1Struct *value = (ParamsCommand1Struct *) params;
因为“params”不能指向“ParamsCommand1Struct”,而是指向“ParamsCommand2Struct”或其他人的指针。
我非常感谢你的意见!
答案 0 :(得分:1)
一般答案
在Writing Solid Code中,Steve Macguire的建议是针对特定情况选择不同的功能(方法)。原因是您可以断言与特定情况相关的条件,并且您可以更容易地调试,因为您有更多的上下文。
一个有趣的例子是用于动态内存分配的标准C运行时函数。其中大部分都是多余的,因为realloc
实际上可以(几乎)完成所需的一切。如果您有realloc
,则不需要malloc
或free
。但是当你有这样一个通用函数,用于几种不同类型的操作时,很难添加有用的断言,并且编写单元测试更加困难,并且在调试时很难看到发生了什么。 Macquire更进一步,并建议realloc
不仅应该执行_re_allocation,而且应该是两个不同的函数:一个用于增长块,一个用于缩小块。
虽然我普遍同意他的逻辑,但有时候有一个通用方法(通常在操作是高度数据驱动的时候)有实际的好处。所以我通常会根据具体情况做出决定,偏向于创建非常具体的方法而不是过于通用的方法。
具体答案
在您的情况下,我认为您需要找到一种方法来从公式中分解出公共代码。 switch
通常是一个信号,您应该使用具有虚函数的小类层次结构。
如果您喜欢单一方法方法,那么它可能只是更具体方法的调度程序。换句话说,switch语句中的每个case都只调用相应的Method1
,Method2
等。如果您希望用户只看到通用方法,那么您可以进行具体实现私人方法。
答案 1 :(得分:1)
通常,提供单独的函数会更好,因为它们的原型名称和参数可以直接和可见地与可用的用户进行通信;这也会导致更直接的文档。
我有一次使用多功能函数就像查询()函数,其中一些次要的查询函数,而不是导致函数的扩散,被捆绑成一个,具有通用输入和输出无效指针。
一般情况下,考虑一下您尝试通过API原型本身与API用户进行沟通的内容;清楚地了解API可以做什么。他不需要过多的minutae;他确实需要知道核心功能,这些功能是首先拥有API的全部要点。
答案 2 :(得分:0)
首先,您需要决定使用哪种语言。同时使用C
和C++
标记问题毫无意义。我假设是C ++。
如果可以创建一个通用函数,那么当然这是更可取的(为什么你更喜欢多个冗余函数?)问题是;你能?但是,您似乎不知道模板。我们需要看看你在这里省略了什么来判断你是否适合模板:
//使用那些特定参数构造一个字符串(命令)(params-> element1,...)
在一般情况下,假设模板是合适的,所有这些都变为:
template <typename T>
unsigned char *Method(CommandTypeEnum command, T *params) {
// more here
}
另外,buffer
如何宣布?您是否返回指向动态分配内存的指针?首选RAII类型的对象,并避免动态分配内存,如果是这样的话。
答案 3 :(得分:0)
如果您使用的是C ++,那么我会避免使用void *,因为您并不需要。使用多种方法没有任何问题。请注意,您实际上不必在第一组示例中重命名该函数 - 您可以使用不同的参数重载函数,以便为每种类型提供单独的函数签名。最终,这类问题非常主观,并且有许多方法可以做。查看第一种类型的函数,通过研究模板函数的使用可能会很好地服务
答案 4 :(得分:0)
您可以创建一个结构。这就是我用来处理控制台命令的方法。
typedef int (* pFunPrintf)(const char*,...);
typedef void (CommandClass::*pKeyFunc)(char *,pFunPrintf);
struct KeyCommand
{
const char * cmd;
unsigned char cmdLen;
pKeyFunc pfun;
const char * Note;
long ID;
};
#define CMD_FORMAT(a) a,(sizeof(a)-1)
static KeyCommand Commands[]=
{
{CMD_FORMAT("one"), &CommandClass::CommandOne, "String Parameter",0},
{CMD_FORMAT("two"), &CommandClass::CommandTwo, "String Parameter",1},
{CMD_FORMAT("three"), &CommandClass::CommandThree, "String Parameter",2},
{CMD_FORMAT("four"), &CommandClass::CommandFour, "String Parameter",3},
};
#define AllCommands sizeof(Commands)/sizeof(KeyCommand)
分析器功能
void CommandClass::ParseCmd( char* Argcommand )
{
unsigned int x;
for ( x=0;x<AllCommands;x++)
{
if(!memcmp(Commands[x].cmd,Argcommand,Commands[x].cmdLen ))
{
(this->*Commands[x].pfun)(&Argcommand[Commands[x].cmdLen],&::printf);
break;
}
}
if(x==AllCommands)
{
// Unknown command
}
}
我使用线程安全的printf pPrintf,所以请忽略它。
答案 5 :(得分:0)
我真的不知道你想做什么,但是在C ++中你可能应该从Formatter Base类中派生出多个类,如下所示:
class Formatter
{
virtual void Format(unsigned char* buffer, Command command) const = 0;
};
class YourClass
{
public:
void Method(Command command, const Formatter& formatter)
{
formatter.Format(buffer, command);
}
private:
unsigned char* buffer_;
};
int main()
{
//
Params1Formatter formatter(/*...*/);
YourClass yourObject;
yourObject.Method(CommandA, formatter);
// ...
}
这消除了处理你班级中所有params内容并使其成为closed for changes的责任。如果在进一步开发过程中会有新的命令或参数,则不必修改(并最终破坏)现有代码,而是添加实现新内容的新类。
答案 6 :(得分:0)
虽然没有完整答案,但这应该指导你正确的方向:一个功能一个责任。首选代码只负责一件事并且做得好。你需要把void void *转换成其他类型的代码那么大的switch语句(它本身并不坏)是一种气味。
顺便说一下,我希望你能意识到按照标准,你只能从void *转换为&lt; type&gt; *仅当原始演员表完全来自&lt; type&gt; *无效*。