2013-02-12 69 views
3

我看過類似的線程,但不知道如何將解決方案完全應用於我的案例。我的問題是,我有一套usecases可以說'A','B','C',當輸入通過時需要執行某些命令(2個用例是輸入)是列出的用例中的任意兩個。例如:是否有任何設計模式可以避免嵌套開關情況?

switch(input1) 
{ 
case A: 
break; 
case B: 
break; 
case C: 
break; 
} 

在每一種情況下的內部,我將不得不檢查輸入2, 所以,最終的代碼看起來是這樣

switch(input1) 
{ 
case A: 
{ 
switch(input2): 
case B: 
break; 
case c: 
break; 
} 
case B: 
{ 
switch(input2): 
case A: 
break; 
case c: 
break; 
} 
.... 

} 

我想使用地圖上(對,命令)並刪除此開關的情況下,但有沒有其他更好的解決方案或設計問題來解決這個問題?

+3

這個問題可能會有所幫助。 http://stackoverflow.com/questions/126409/ways-to-eliminate-switch-in-code – RDX 2013-02-12 07:59:43

+1

對於鏈接答案中的多態性+1。 – Najzero 2013-02-12 08:04:45

+0

這些「usecases」,他們是一種狀態?所以你的類是一個狀態機,它在執行期間改變它的狀態('input1') – leemes 2013-02-12 08:15:05

回答

8

如果性能是不是那是個大問題,那麼函數指針的映射就可能是一個解決方案。

假設標籤ABC ...比255整數值。

  • 設置地圖第一

    #define KEY(a,b) ((a<<8) | b) 
    
    std::map<int, function_pointer_type> dispatcher = 
    { 
        { KEY(A,B), ab_handler}, 
        { KEY(A,C), ac_handler}, 
        { KEY(B,C), bc_handler}, 
        //etc 
    }; 
    
  • 使用地圖調用相應的處理程序爲每個組輸入:

    dispatcher[KEY(input1,input2)] (/* args */); 
    

請注意,您必須設置調度與每個可能的一對輸入。另外,如果對KEY(A,B)KEY(B,A)是相同的情況,那麼您可以編寫一個名爲invoke的函數來處理這種情況,以便爲其餘代碼提供統一的用法。

void invoke(int input1, int input2, /* args */) 
{ 
    if (dispatcher.find(KEY(input1, input2)) != dispatcher.end()) 
      dispatcher[KEY(input1,input2)] (/* args */); 
    else 
      dispatcher[KEY(input2,input1)] (/* args */); 
} 

然後用它作爲:

invoke(input1, input2, /* args */); 
invoke(input2, input1, /* args */); //still okay! 

希望有所幫助。

+1

對於「雙調度員」對稱性的+1。 – leemes 2013-02-12 08:24:26

+0

'std :: pair '是關鍵問題? (如果你想打開這個鍵,你的宏很有用,但是它不需要映射。) – 2013-02-12 08:47:39

+1

另外:我想不出一個'A','B'會有相同的情況行爲爲「B」,「A」,但誰知道。如果是這樣的話,那麼最簡單的解決方案是不是將兩個條目放入地圖中? – 2013-02-12 08:49:15

2

在你的情況下,如何對兩個開關分成兩個功能

bool processInput2(char input2) 
{ 
    switch(input2) 
    { 
    case 'A': 
    { 
     // blah 
    } 
    break; 
} 

bool processInput1(char input1) 
{ 
    switch(input1) 
    { 
    case 'A': 
     processInput2(input2); 
     break; 
} 
+0

正如我所理解的問題,每個可能的'input1'都會有一個'processInput2'函數。所以會有,例如,'processInput2ForA','... ForB'等等 – leemes 2013-02-12 08:12:03

+0

它取決於...但問題是模糊的。這是我可以提供的簡單解決方案 – billz 2013-02-12 08:18:33

-1

爲什麼不如果使用的支化?

if (input1 == A && input2 == B) { 
} else if (input1==A && input2 = C) { 
} ... 

這是寫你的意思。

+0

如果您花時間進行downvote,請抽出時間發表評論來解釋自己。 – wirrbel 2017-06-21 20:40:14

1

一種可能性是將代碼分割成每嵌套情況下,一個功能,所以你的例子是有6個功能:

void process_A_A() { ... } 
void process_A_B() { ... } 
void process_B_A() { ... } 
void process_B_B() { ... } 
void process_C_A() { ... } 
void process_C_B() { ... } 

然後,把它們放進一個陣列中初始化爲非常快速的(常數時間)在運行時查找:

typedef std::function<void(void)> Function; // or: void (*)(void) 
Function f[Input1Count][Input2Count]; 
f[A][A] = &process_A_A; 
f[A][B] = &process_A_B; 
... 

調用適當的功能,寫:

f[input1][input2](); 

請注意,通過使用C++ 11 std::function類型,函數不必是經典函數指針;它們也可以是lambda函數或函子對象。

您也可以保留一些零件爲空或多次分配相同的功能。當你決定要保留一些條目空(所以不應該有任何在這種情況下完成的),並稱其前檢查函數對象:

if (f[input1][input2]) 
    f[input1][input2](); 
0

你總是可以這樣做:

switch (256 * input1 + input2) { 
case 256 * 'A' + 'B': 
    // ... 
    break; 
// ... 
}; 

但坦率地說,在這種情況下,我會找到嵌套開關容易 理解,假設switch是正確的答案 您的問題。對於字符輸入而言,它通常是,但也有 其他替代方法,如std::map<std::pair<char, char>, Action const*>,其中Action是虛擬基類,並且 映射中的每個操作都是派生類的靜態實例。 這樣做的好處是可以使每個動作成爲不同的對象 (這可能不是一個優點,具體取決於您在動作中執行的操作),並且如果地圖是動態填充的(例如 在Action的構造函數中),您可以添加修改解析器源代碼的操作(但這種靈活性可能不需要 )。

0

建議的地圖或指針處理函數表的答案是好的。但我看到兩個缺點: 1)與手動嵌套開關相比,性能下降較小。 2)案件處理方法不完全自我描述。我的意思是你必須提及每個處理方法兩次 - 在其定義和你創建地圖的地方。

我看到兩個備選方案: 1)源代碼生成。從某種表示自動生成嵌套開關。那麼......如果不介意爲這麼小的任務添加代碼生成,那麼創建最佳代碼是非常好的選擇。 2)使用預處理器黑客。不是最優雅,但相當有趣的方式,使其工作。

首先聲明X-Macro我們的枚舉:

#define ELEMENTS(processor) \ 
processor(firstElement)  \ 
processor(secondElement) \ 
processor(thirdElement) 

我們可以用它來聲明枚舉本身:

#define ENUM_PROCESSOR(arg) arg, 

enum class 
{ 
    ELEMENTS(ENUM_PROCESSOR) 
}; 

#undef ENUM_PROCESSOR 
Now we can add method that uses macros to generate nested switches: 

void processElements(const Elements element1, 
        const Elements element2) 
{ 
    // These macros are useful to trick the preprocessor to allow recursive macro calls 
    // https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms 
    #define EMPTY(...) 
    #define DEFER(...) __VA_ARGS__ EMPTY() 
    #define EXPAND(...) __VA_ARGS__ 
    #define ELEMENTS_INT() ELEMENTS 

    #define PROCESS_NESTED_ENUM_VALUE(value)           \ 
    case Elements::value:               \ 
    {                    \ 
     process<Value1, Elements::value>();           \ 
     break;                  \ 
    } 

    #define PROCESS_ENUM_VALUE(value)            \ 
    case Elements::value:               \ 
    {                    \ 
     constexpr Elements Value1 = Elements::value;         \ 
     switch (element2)               \ 
     {                   \ 
      DEFER(ELEMENTS_INT)()(PROCESS_NESTED_ENUM_VALUE)       \ 
     };                   \ 
                        \ 
     break;                  \ 
    } 

    switch (element1) 
    { 
     EXPAND(ELEMENTS(PROCESS_ENUM_VALUE)); 
    }; 

    #undef EMPTY 
    #undef DEFER 
    #undef EXPAND 

    #undef ELEMENT_TYPES_INT 
    #undef PROCESS_ENUM_VALUE 
    #undef PROCESS_NESTED_ENUM_VALUE 
} 

很多拓展力度這裏都是爲了「絕招」預處理器ELEMENTS遞歸。主要想法很好地描述了here

現在,我們宣佈我們的處理程序爲模板功能專業化:

template <Elements Element1, Elements Element2> 
void process(); 

template<> 
void process<Elements::firstElement, Elements::firstElement>() 
{ 
    //some code 1; 
} 

... 
相關問題