2009-11-11 76 views
2

我所維護的代碼中有許多函數,它們可能會被描述爲樣板重。這裏是一個與遊標處理數據庫I/O時,重複整個應用程序的廣告噁心的樣板模式:有沒有更好的方法? While循環並繼續

if(!RowValue(row, m_InferredTable->YearColumn(), m_InferredTable->YearName(), m_InferredTable->TableName(), value) 
     || !IsValidValue(value)) 
    { 
     GetNextRow(cursor, m_InferredTable); 

     continue; 
    } 
    else 
    { 
     value.ChangeType(VT_INT); 
     element.SetYear(value.intVal); 
    } 

的事情是不是所有的這些陳述是這樣處理整數的,這個「元素」的對象, 「年」專欄等。我被要求比現在更凝結它,我想不出一種辦法。我繼續跳過繼續聲明和各個類的訪問器。

編輯:感謝所有那些評論。這就是爲什麼我喜歡這個網站。這裏是一個擴大視圖:

while(row != NULL) 
{ 
    Element element; 
    value.ClearToZero(); 
    if(!GetRowValue(row, m_InferredTable->DayColumn(), m_InferredTable->DayName(), m_InferredTable->TableName(), value) 
     || !IsValidValue(value)) 
    { 
     GetNextRow(cursor, m_InferredTable); 

     continue; 
    } 
    else 
    { 
     value.ChangeType(VT_INT); 
     element.SetDay(value.intVal); 
    } 

而事情繼續這樣下去。並非所有取自「行」的值都是整數。 while循環中的最後一個子句是「GetNextRow」。

+0

你能比後一個例子多的話,所以我們可以看到什麼是固定的,哪些更容易改變? – tragomaskhalos 2009-11-11 15:02:16

+0

我們需要看到更多的結構......這顯然是一個循環中的一個子句,但是在一個循環中有多少個這樣的子句?每個測試(如果)不同? – 2009-11-11 15:04:26

+0

在目前的形式下,很難理解這個問題。爲什麼有一個「else」分支,如果真正的分支以「continue」結尾?週期內的if後面還有別的東西嗎?該週期在哪裏? – AnT 2009-11-11 15:07:20

回答

4

好了,從你說過,你有一個結構是這樣的:

while (row!=NULL) { 
    if (!x) { 
     GetNextRow(); 
     continue; 
    } 
    else { 
     SetType(someType); 
     SetValue(someValue); 
    } 
    if (!y) { 
     GetNextRow(); 
     continue; 
    } 
    else { 
     SetType(SomeOtherType); 
     SetValue(someOtherValue); 
    } 
// ... 

    GetNextRow(); 
} 

如果這是真的,我會擺脫所有GetNextRow調用除了最後一個。然後我會結構中的代碼是這樣的:

while (row != NULL) { 
    if (x) { 
     SetType(someType); 
     SetValue(someValue); 
    } 
    else if (y) { 
     SetType(someOtherType); 
     SetValue(SomeOtherValue); 
    } 
    // ... 
    GetNextRow(); 
} 

編輯:另一種可能性是寫你的代碼作爲一個循環:

for (;row!=NULL;GetNextRow()) { 
    if (!x) 
     continue; 
    SetTypeAndValue(); 
    if (!y) 
     continue; 
    SetTypeandValue(); 
    // ... 

由於調用GetNextRow現在是循環的一部分,本身,我們不必每次都(明確地)調用它 - 循環本身會處理這個問題。下一步(如果你有足夠的這樣做值得)將縮短代碼來設置類型和值。一種可能性是使用模板特:

// We never use the base template -- it just throws to indicate a problem. 
template <class T> 
SetValue(T const &value) { 
    throw(something); 
} 

// Then we provide a template specialization for each type we really use: 
template <> 
SetValue<int>(int value) { 
    SetType(VT_INT); 
    SetValue(value); 
} 

template <> 
SetValue<float>(float value) { 
    SetType(VT_FLOAT); 
    SetValue(value); 
} 

這可以讓你結合一對呼叫設置類型和值到一個單一的通話。

編輯:至於切削加工短變,這取決於 - 如果解析列是昂貴的(足夠的關心),你可以簡單地嵌套的條件:

if (x) { 
    SetTypeAndValue(); 
    if (y) { 
     SetTypeAndValue(); 
     if (z) { 
      SetTypeAndValue(); 

等。這樣做的一個主要缺點是,如果(如你所說)在單個循環中有20多個條件,它會變得非常深。既然如此,我可能會認真考慮上面給出的基於for循環的版本。

+0

所以,只要說如果我的一個列返回一個無效值只是繼續解析所有的列值,最後放棄行的條目,然後繼續到下一行?謝謝。 – wheaties 2009-11-11 15:40:51

0

爲什麼不反轉你的if-test?

if (RowValue(row, m_InferredTable->YearColumn(), m_InferredTable->YearName(), m_InferredTable->TableName(), value) 
    && IsValidValue(value)) 
    { 
    value.ChangeType(VT_INT); 
    element.SetYear(value.intVal); 
    } 
    else 
    { 
    GetNextRow(cursor, m_InferredTable); 
    } 
+0

您遺漏了continue語句。這是這裏的一大懸念 - 如何獲得樣板代碼以繼續循環。 – mskfisher 2009-11-11 15:04:47

+0

「繼續」的需要意味着這是循環中許多子句中的一個。我們仍在等待OP的確認,但這聽起來像是一個繼續執行的大案例聲明。 – 2009-11-11 15:08:01

+0

我希望這不是一個大案例陳述。然而,是的,繼續是殺死它。如果從其中一列取得的值無效,則continue將導致循環轉到查詢的下一行。 – wheaties 2009-11-11 15:27:01

0

我本能的做法是在這裏建立一個多態的方法,在那裏你最終風做這樣的事情(模數你的語言和詳細的邏輯):

db_cursor cursor; 

while(cursor.valid()) 
{ 
    if(cursor.data.valid()) 
    { 
    process(); 
    } 

    cursor.next(); 
} 

db_cursor將是一個基類,你不同的表類型繼承自子類,並且子類將實現有效性函數。

+0

如果遊標和行對象來自第三方API,該怎麼辦? – wheaties 2009-11-11 15:22:10

+0

將它們包裝在我自己的對象中以實現通用接口。這將是混亂的,你將不得不小心鑽石繼承問題(取決於),但從長遠來看它會更乾淨。 – 2009-11-11 15:31:53

0

將它移到一個模板函數中,以模板的元素類型(例如整數)爲模板,您可以反覆調用該函數。使用特徵模板改變每種數據類型的行爲。

template <typename T> struct ElemTrait<T> {}; 
template <> struct ElemTrait<int> { 
    static inline void set(Val &value, Elem &element) { 
     value.ChangeType(VT_INT); 
     element.SetYear(value.intVal); 
    } 
}; 
// template <> struct ElemTrait<float> { ... }; 

template <typename T> 
void do_stuff(...) { 
    // ... 

    if (!RowValue(row, 
     m_InferredTable->YearColumn(), 
     m_InferredTable->YearName(), 
     m_InferredTable->TableName(), value) 
     || !IsValidValue(value) 
    ) { 
     GetNextRow(cursor, m_InferredTable); 
     continue; 
    } else { 
     ElemTrait<T>::set(value, element); 
    } 

    // ... 
} 
0

你可以拿出所有的GetNextRow調用和else子句:

for (row = GetFirstRow() ; row != null ; GetNextRow()) 
{ 
    Element element; 
    value.ClearToZero(); 
    if(!GetRowValue(row, m_InferredTable->DayColumn(), m_MetInferredOutTable->DayName(), m_MetInferredOutTable->TableName(), value) 
      || !IsValidValue(value)) 
    { 
     continue; 
    } 

    value.ChangeType(VT_INT); 
    element.SetDay(value.intVal); 
} 
2

爲什麼不做一個功能來做所有的工作?

bool processElement(Element& element, Row* row, int value, Table& m_InferredTable, /*other params*/) 
{ 
    if(!GetRowValue(row, m_InferredTable->DayColumn(), m_InferredTable->DayName(), m_InferredTable->TableName(), value) 
      || !IsValidValue(value)) 
    { 
      GetNextRow(cursor, m_InferredTable); 
      return true; 
    } 
    else 
    { 
      value.ChangeType(VT_INT); 
      element.SetDay(value.intVal); 
    } 
    return false; 
} 

在你的循環

while (row != NULL) 
{ 
    if (processElement(element, row, value, m_InferredTable)) 
     continue; 
    // other code 
} 
+0

我需要爲每個我想要的值創建一個函數,對嗎?並非所有值都是整數。有日,年,小時,另加24種其他類型。該死的我想念反思。 – wheaties 2009-11-11 15:44:11

+0

你可以使它成爲處理所有類型的模板函數... – Inverse 2009-11-11 16:02:13

+0

@反向:每種類型在'else'子句中可能都有它自己的唯一代碼,所以模板在這裏可能沒有幫助。函數指針也許? – Skizz 2009-11-11 16:49:29