2012-05-25 77 views
2

我有一個問題,讓我覺得很蠢。在一個業餘愛好項目中,我有一個std ::指向接口類的指針列表,指向接口的各種具體實現。沒有RTTI的計數對象

例如,說我有以下幾點:

class Seafood ... 
class Fishstick : public Seafood ... 
class Squid : public Seafood ... 
... 
std::list<Seafood*> buffet; 

現在,我有我的自助不同的海鮮物品填充,我想指望我有可供看是否更需要fishsticks數量從廚房訂購。

我該如何做到這一點沒有RTTI或一些不正當的實施?我讀過一些文章,聲稱如果你發現自己想要使用RTTI,你會以錯誤的方式接近OOP,或者你的解決方案應該重新設計。有沒有解決這個問題的模式或其他解決方案?我相信我以前必須多次出現。我想到了這是一種明顯的虛擬功能,但我無法弄清楚如何在沒有構建RTTI的俗套版本或關於後代進入界面的某些知識(CountIfFishstick/IsFishstick /是(種))。

編輯:想到的另一件事是保留一份魚餌列表,一份魷魚列表等,但這肯定會打敗接口/實現的整個目的。

+1

正如你所說,這裏有一個小小的代碼味道:要麼你關心特定類型(你似乎)或者你不關心(使用界面)。請注意,多態不是能夠將它們一起存儲在容器中,而是能夠通過固定接口(例如調用函數時)使用派生類型,因此您可能希望將它們分開(用於計算目的)和但使用界面或其他目的。 –

+0

如果你絕對必須檢查變量的類型以確定它的性質,'bool isFishstick = !! dynamic_cast (value);'應該工作。也就是說,你應該尋找其他解決這個問題的方法。 –

+2

實際上,所有人都害怕RTTI等,這實際上很愚蠢。氣味並不表示不好的設計,這表明可能是壞的設計。雖然多態性是關於對待事物是相同的,但有時候這是必要的,比如在計算具體類型時,要挖掘繼承層次。這樣做而不是其他一些令人費解的事情是最簡單和最好的方法。 –

回答

3

您可能想要訪問者模式的一些變體。有很多,很難說你想要什麼。我可能會建議獲取Modern C++ Design並回顧Alexendrescu的實現。否則,谷歌「訪問者模式」,你會得到1000公里的鏈接閱讀。

+0

我可以在4分鐘內接受這個答案,但是。這種模式會很好地工作。我會在我的問題上擴展以回答您的建議。 – Piotr

1

Visitor pattern是你在找什麼。還有一個特殊版本的訪問者,名爲Acyclic visitor,它使用RTTI解決了原始訪問者的一些問題,所以RTTI並不總是錯的,但它可能導致可怕的代碼,除非你真的知道你在做什麼。 ..

0

如果您正在使用C++ 11,你可以這樣做:

int num_fish_sticks = std::count_if(buffer.begin(), buffet.end(), 
    [](const SeaFood* sf) {sf->is_fish_stick()}); 

您需要在海鮮聲明一個純虛函數:

virtual bool is_fish_stick() const = 0; 

並實施一致在子類中。

編輯:當然,如果你有太多的子類,這可能是混亂的。在這種情況下,你最好用RTTI:

int num_fish_sticks = std::count_if(buffer.begin(), buffet.end(), 
    [](const SeaFood* sf) {typeid(*sf) == typeid(Fishstick)}); 
+0

這比使用RTTI更糟糕。 –

+0

@AndreasMagnusson好,OP想要一個解決方案,而不使用RTTI。我將使用RTTI更新我的答案,以包含更通用的方法。 – betabandido

+0

是的,對於一個很好的解決方案,請參閱我的和Crazy Eddies的迴應。訪問者模式是一種在不改變原始類的情況下將新虛擬函數「添加」到類層次結構的方法。 –

0

我認爲你的問題比你看起來更基本:至少你已經提出過,似乎沒有在這裏根本就沒有什麼(任何?)理由繼承。

當你的對象具有不同的行爲時,繼承是有用的,但在這種情況下,它們似乎都具有基本相同的行爲 - 事實上,幾乎沒有行爲(除非計數爲「死亡」) 。

如果我要去模特自助餐,我大概使用容器。熱桌上的每個托盤都是一個容納一定量東西的容器。如果你願意,你可以將表格建模爲托盤的容器,並且(可能)將該房間作爲表格的容器。

每個托盤上都有一個「食品」(或任何您喜歡稱之爲的食品),對此您大多都有一個名稱和數量。唯一的行爲可能是一個計時器,處理食物何時被「服務」,並根據這個計時器來解決剩下的需求。你可能有一點比這更多,比如將食物分成只能作爲正餐的一部分的那些食物,與那些只能購買沙拉吧的人分享食物。

1

假設你在基類中有一個name函數來返回項目的名稱,所以你可以將它顯示到廚房。只需使用它來索引項目計數的地圖。

一般來說,您可以提供一個函數,它返回每個類的任何唯一標識符。

1

複合圖案如何?自助餐真的是一個海鮮收藏品的集合。 FishStick和Squid是複合模式中的「組件」,它將保持它們的項目數量。所以當自助餐列表時,它可以遍歷和調用複合材料。