2010-12-11 74 views
23

我最近得知開關語句在OOP中很糟糕,特別是Robert Martin的「Clean Code」(p37-39)。但是考慮一下這個場景:我正在寫一個遊戲服務器,接收來自客戶端的消息,其中包含一個表示玩家行爲的整數,例如移動,攻擊,挑選物品等等,將會有超過30個不同的行動。當我編寫處理這些消息的代碼時,不管我想到什麼解決方案,它都必須在某處使用切換。如果沒有切換語句,我應該使用什麼模式?開關語句不好?

+0

請參閱[Large Switch statements:Bad OOP? (http://stackoverflow.com/questions/505454/large-switch-statements-bad-oop) – 2010-12-11 14:01:04

+0

總是覺得這很有趣,因爲明顯改變的代碼是在switch語句中添加另一個案例,但添加另一個類不是。 ..有時OO純粹主義者可以得到一點「宗教」... – 2010-12-11 14:04:20

+3

'「認爲有害的」認爲有害的「。 – delnan 2010-12-11 14:04:26

回答

21

交換機就像任何其他控制結構。有些地方是最好的/最乾淨的解決方案,還有更多的地方完全不合適。這只是比其他控制結構更多的濫用方式。

在OO設計中,在像你這樣的情況下,通常認爲使用不同的消息類型/繼承自公共消息類的類,然後使用重載方法「自動」區分不同類型。

在像您這樣的情況下,您可以使用映射到您的操作代碼的枚舉,然後將屬性附加到每個枚舉值,這樣您就可以使用泛型或類型構建來構建不同的Action子類對象,重載方法將起作用。

但這是一個真正的痛苦。

評估是否有設計選項,例如您的解決方案中可行的枚舉。如果不是,只需使用開關。

+1

我想你的意思是動態多態而不是靜態多態(*重載的方法*) – Geek 2013-03-17 16:26:18

+0

@Toby您能否用下面的例子來詳細說明這個語句:「然後給每個枚舉值附加一個屬性,讓您使用泛型或類型構建來構建不同的Action子類對象,以便重載方法可以工作。「? – beinghuman 2017-09-14 06:00:11

13

想到Strategy模式。

策略模式旨在提供一種方法來定義一系列算法,將每個算法封裝爲一個對象,並使它們可以互換。策略模式可以讓算法獨立於使用它們的客戶端。

在這種情況下,「算法家族」是您的不同行爲。


至於switch語句 - 在「清潔守則」,羅伯特·馬丁說,他試圖把自己限制在每個類型一個 switch語句。不完全消除它們。

原因是switch語句不符合OCP

3

我會把這些消息放入一個數組中,然後將該項目與解決方案關鍵字進行匹配以顯示該消息。

+0

並非所有的東西都是OOP。真的很喜歡這個答案。 – grasshopper 2014-02-07 09:57:24

14

'壞'開關語句通常是那些開關對象類型(或者可能是另一種設計中的對象類型)的開關語句。換句話說,硬編碼可能更好地被多態處理。其他類型的開關語句可能是好的

您將需要一個switch語句,但只有一個。當你收到消息時,調用一個Factory對象來返回適當的消息子類(Move,Attack等)的對象,然後調用message-> doit()方法來完成這項工作。

這意味着如果添加更多消息類型,只有工廠對象必須更改。

+0

「Map ,Thing」這樣的事情怎麼樣?'這與「做班級切換」非常相似,但是,這是否被認爲是一種好習慣? – YoTengoUnLCD 2016-06-20 20:14:33

4

從設計模式的角度來看,您可以針對給定的場景使用命令模式。 (見http://en.wikipedia.org/wiki/Command_pattern)。

如果您發現自己在OOP範例中反覆使用switch語句,這表示您的類可能設計不好。假設你有一個適當的super和sub類設計和相當數量的多態性。 switch語句後面的邏輯應該由子類來處理。

有關如何刪除這些switch語句並介紹正確的子類的更多信息,我建議您閱讀Martin Fowler的第一章「重構」。或者你可以在這裏找到類似的幻燈片http://www1.informatik.uni-wuerzburg.de/database/courses/pi2_ss03_dir/RefactoringExampleSlides.pdf。 (Slide 44)

2

IMO switch陳述不是不好,但應儘可能避免。一種解決方案是使用Map,其中鍵是命令,值Command對象使用​​方法。或者如果您的命令是數字且沒有空白,則爲List

但是,通常,在實現設計模式時,您會使用switch語句;一個例子是使用Chain of responsibility模式來處理給定任何命令「id」或「value」的命令。 (也提到了Strategy模式。)但是,就您的情況而言,您還可以查看Command模式。

基本上,在OOP中,您將嘗試使用除依賴switch塊之外的其他解決方案,這些塊使用過程式編程範例。但是,什麼時候以及如何使用都是您的決定。

  • 封裝是一組具有coherant API類(例如::Collection API使用Factory圖案等


    代碼組織的定義是當我個人經常使用switch塊在許多框架中)

  • a類是一組相關功能(例如:Math類...
  • 方法a功能;它應該只做一件事和一件事。 (例如:在列表中添加項目可能需要放大該列表,在這種情況下,add方法將依賴於其他方法來執行此操作,並且不會執行該操作本身,因爲它不是合同。)

因此,如果您switch語句執行各種不同的操作,你是「違反」這一定義;而使用設計模式並不是每個操作都在其自己的類中定義的(它是自己的一組功能)。

1

使用命令。將動作包裹在一個對象中,讓多態爲你做開關。在C++中(shared_ptr只是一個指針,或者是Java中的引用。它允許動態分配):

void GameServer::perform_action(shared_ptr<Action> op) { 
    op->execute(); 
} 

客戶挑選要執行的操作,而一旦他們這樣做,他們發送的行動本身到服務器,因此服務器不需要做任何分析:

void BlueClient::play() { 
    shared_ptr<Action> a; 
    if(should_move()) a = new Move(this, NORTHWEST); 
    else if(should_attack()) a = new Attack(this, EAST); 
    else a = Wait(this); 
    server.perform_action(a); 
} 
1

我不買它。這些OOP狂熱分子似乎擁有無限RAM和驚人性能的機器。顯然,對於無限RAM,您不必擔心內存碎片以及連續創建和銷燬小助手類時的性能影響。爲了解釋「美麗的代碼」一書的引用 - 「計算機科學中的每一個問題都可以用另一個抽象層次來解決」

如果你需要的話,可以使用開關。編譯器非常擅長爲它們生成代碼。

+2

你應該重構然後優化。反過來這樣做沒有任何意義。 – Eva 2013-01-27 20:30:20