我最近得知開關語句在OOP中很糟糕,特別是Robert Martin的「Clean Code」(p37-39)。但是考慮一下這個場景:我正在寫一個遊戲服務器,接收來自客戶端的消息,其中包含一個表示玩家行爲的整數,例如移動,攻擊,挑選物品等等,將會有超過30個不同的行動。當我編寫處理這些消息的代碼時,不管我想到什麼解決方案,它都必須在某處使用切換。如果沒有切換語句,我應該使用什麼模式?開關語句不好?
開關語句不好?
回答
交換機就像任何其他控制結構。有些地方是最好的/最乾淨的解決方案,還有更多的地方完全不合適。這只是比其他控制結構更多的濫用方式。
在OO設計中,在像你這樣的情況下,通常認爲使用不同的消息類型/繼承自公共消息類的類,然後使用重載方法「自動」區分不同類型。
在像您這樣的情況下,您可以使用映射到您的操作代碼的枚舉,然後將屬性附加到每個枚舉值,這樣您就可以使用泛型或類型構建來構建不同的Action子類對象,重載方法將起作用。
但這是一個真正的痛苦。
評估是否有設計選項,例如您的解決方案中可行的枚舉。如果不是,只需使用開關。
我想你的意思是動態多態而不是靜態多態(*重載的方法*) – Geek 2013-03-17 16:26:18
@Toby您能否用下面的例子來詳細說明這個語句:「然後給每個枚舉值附加一個屬性,讓您使用泛型或類型構建來構建不同的Action子類對象,以便重載方法可以工作。「? – beinghuman 2017-09-14 06:00:11
我會把這些消息放入一個數組中,然後將該項目與解決方案關鍵字進行匹配以顯示該消息。
並非所有的東西都是OOP。真的很喜歡這個答案。 – grasshopper 2014-02-07 09:57:24
'壞'開關語句通常是那些開關對象類型(或者可能是另一種設計中的對象類型)的開關語句。換句話說,硬編碼可能更好地被多態處理。其他類型的開關語句可能是好的
您將需要一個switch語句,但只有一個。當你收到消息時,調用一個Factory對象來返回適當的消息子類(Move,Attack等)的對象,然後調用message-> doit()方法來完成這項工作。
這意味着如果添加更多消息類型,只有工廠對象必須更改。
「Map
從設計模式的角度來看,您可以針對給定的場景使用命令模式。 (見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)
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
語句執行各種不同的操作,你是「違反」這一定義;而使用設計模式並不是每個操作都在其自己的類中定義的(它是自己的一組功能)。
使用命令。將動作包裹在一個對象中,讓多態爲你做開關。在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);
}
我不買它。這些OOP狂熱分子似乎擁有無限RAM和驚人性能的機器。顯然,對於無限RAM,您不必擔心內存碎片以及連續創建和銷燬小助手類時的性能影響。爲了解釋「美麗的代碼」一書的引用 - 「計算機科學中的每一個問題都可以用另一個抽象層次來解決」
如果你需要的話,可以使用開關。編譯器非常擅長爲它們生成代碼。
你應該重構然後優化。反過來這樣做沒有任何意義。 – Eva 2013-01-27 20:30:20
- 1. 開關語句
- 2. 開關語句
- 3. 開關語句
- 4. PHP開關語句
- 5. C++開關語句
- 6. 開關語句Message.Contains
- 7. Perl開關語句
- 8. Java開關語句
- 9. 非斷開開關語句
- 10. 如果語句選擇開關語句
- 11. 開關語句不像它應該
- 12. 開關語句不被識別
- 13. 開關語句不起作用
- 14. 枚舉不爲換開關語句
- 15. 開關語句不起作用。 C#
- 16. android - 重複開關語句
- 17. 多參數開關語句
- 18. 內部開關語句mpdf
- 19. 開關語句繼續
- 20. 開關語句問題(Matlab)
- 21. 開關()語句用法
- 22. 重構長開關語句
- 23. 陣列開關case語句
- 24. 多條件開關語句?
- 25. PHP開關語句錯誤
- 26. 凝聚開關語句?
- 27. 重構開關 - 語句
- 28. SRSS中的開關語句
- 29. 嵌套開關語句
- 30. 開關語句在php
請參閱[Large Switch statements:Bad OOP? (http://stackoverflow.com/questions/505454/large-switch-statements-bad-oop) – 2010-12-11 14:01:04
總是覺得這很有趣,因爲明顯改變的代碼是在switch語句中添加另一個案例,但添加另一個類不是。 ..有時OO純粹主義者可以得到一點「宗教」... – 2010-12-11 14:04:20
'「認爲有害的」認爲有害的「。 – delnan 2010-12-11 14:04:26