2012-08-29 25 views
2

這是我長時間檢查這個奇妙的網頁後的第一個問題。幾個特定的​​方法或一個通用的方法?

大概我的問題有點傻,但我想知道別人對此的看法。有什麼更好的辦法來創建幾種特定的方法,或者另一方面,只有一種通用的方法?下面是一個例子...

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

你使用C還是C++?答案取決於你實際使用的是哪一種。 –

+0

C++在這種情況下...... – kazbeel

回答

1

一般的答案

Writing Solid Code,史蒂夫Macguire的建議是,喜歡不同的函數(方法)的具體情況。原因是您可以斷言與特定情況相關的條件,並且您可以更容易地進行調試,因爲您有更多的上下文。

一個有趣的例子是動態內存分配的標準C運行時函數。它大部分是多餘的,因爲realloc實際上可以做(幾乎)所有你需要的。如果您有realloc,則不需要mallocfree。但是,當你有這樣一個通用函數,用於幾種不同類型的操作時,很難添加有用的斷言,並且編寫單元測試更加困難,而且在調試時很難看到發生了什麼。麥奎爾向前邁進了一步,並且暗示,不僅realloc只是做了重新分配,而且應該是兩個不同的功能:一個用於增加一個塊,一個用於縮小塊。

儘管我普遍認同他的邏輯,但有時候有時有一個通用目的方法(通常在操作是高度數據驅動的時候)有實際的優點。所以我通常會根據具體情況做出決定,傾向於創建非常具體的方法,而不是過於通用的方法。

具體的答案

在你的情況,我認爲你需要找到一種方法來分解出從具體的公共代碼。 switch通常表示您應該使用具有虛擬功能的小類層次結構。

如果你喜歡單一的方法,那麼它可能應該只是一個更具體的方法調度。換句話說,switch語句中的每種情況只需調用相應的Method1,Method2等。如果您希望用戶只看到通用方法,那麼您可以使具體實現爲私有方法。

0

首先,您需要決定使用哪種語言。在CC++這兩個標記問題沒有意義。我假設C++。

如果你可以創建一個通用函數那麼當然這是可取的(爲什麼你會喜歡多個冗餘函數?)問題是;你可以嗎?但是,你似乎沒有意識到模板。我們需要看到你在這裏沒有告訴你什麼,如果模板是適合不過:

//創建一個字符串(命令)與特定PARAMS(params->部件1,...)

在一般情況下,假設模板是適當的,所有這一切變成:

template <typename T> 
unsigned char *Method(CommandTypeEnum command, T *params) { 
    // more here 
} 

在一個側面說明,如何buffer聲明?你是否返回一個指向動態分配內存的指針?首選RAII類型的對象,並避免像這樣動態分配內存。

+0

在這種情況下,緩衝區是類的成員。這是針對嵌入式系統的,所以我們儘量避免動態分配內存...... – kazbeel

0

如果你使用C++,那麼我會避免使用void *,因爲你並不需要。有多種方法沒有錯。請注意,您實際上不必在第一組示例中重命名該函數,只需使用不同的參數重載函數即可,以便每種類型都有一個單獨的函數簽名。最終,這種問題是非常主觀的,有很多方法可以做事。看看你的第一類函數,你可能會很好的服務於模板函數的使用

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,所以忽略它。

0

我真的不知道你想做的事,但在C++中,你或許應該從格式化基類派生這樣的多個類:

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。如果在進一步開發過程中會有新的命令或參數,則不必修改(並最終打破)現有代碼,而是添加實現新功能的新類。

1

一般來說,最好提供單獨的函數,因爲它們的原型名稱和參數直接和可視地與用戶進行通信;這也導致更直接的文件。

有一次,我使用多用途函數是爲了類似於query()函數,其中一些次要查詢函數,而不是導致泛函的功能,被捆綁成一個,具有通用輸入並輸出void指針。一般來說,想想你想通過API原型本身與API用戶進行通信的內容;清楚API的功能。他不需要過多的minutae;他確實需要知道首先具有API的核心功能。

0

雖然沒有完整的答案,這應該引導你在正確的方向:一個功能一個責任。首選代碼只對一件事負責,並且做得很好。代碼whith巨大的switch語句(這本身並不壞),你需要將void *轉換爲其他類型的代碼是一種氣味。

通過我希望你明白,按照標準,你只能從void *的轉換爲<型> *只有當原班人馬正是從<型> *爲void *的方式。

相關問題