2009-06-02 24 views
2

我在C#/ Winforms中有一個應用程序,它允許用戶在網格上放置對象來爲遊戲創建關卡。它有幾個工具來放置瓷磚/燈/門/實體等。目前,我只是使用枚舉來存儲當前選定的工具並使用switch語句來運行每個工具代碼。由於我一直在嚮應用程序添加更多工具,因此它開始獲得像意大利麪一樣的大量重複代碼。重構C中的大開關的建議#

這裏是按下鼠標功能的縮減版本,在我的編輯器類:

public void OnEditorViewMouseDown(Point mousePos) 
    { 
     // Check if the click is out of bounds. 
     if (IsLocationOOB(mousePos)) return; 

     if (CurrentTool == ToolType.GroundTile) 
     { 
      // Allow drags along whole tiles only. 
      m_DragManager.DragType = DragManager.DragTypeEnum.Tile; 
      m_DragManager.StartDrag(mousePos); 
     } 
     else if (CurrentTool == ToolType.WallTile) 
     { 
      // Allow drags along grid edges only. 
      m_DragManager.DragType = DragManager.DragTypeEnum.Edge; 
      m_DragManager.StartDrag(mousePos); 
     } 
     else if (CurrentTool == ToolType.PostTile) 
     { 
      // Allow drags along grid points only. 
      m_DragManager.DragType = DragManager.DragTypeEnum.Point; 
      m_DragManager.StartDrag(mousePos); 
     } 
     else if (CurrentTool == ToolType.AreaLight) 
     { 
      // Allow drags anywhere. ie. not snapped to the grid in some way. 
      m_DragManager.DragType = DragManager.DragTypeEnum.FreeForm; 
      m_DragManager.StartDrag(mousePos); 
     } 
     else if (CurrentTool == ToolType.PointLight) 
     { 
      m_CurrentWorld.AddLight(TranslateToWorldCoords(mousePos)); 
     } 
     else if (CurrentTool == ToolType.PlaceEntity) 
     { 
      m_CurrentWorld.PlaceEntity(TranslateToWorldCoords(mousePos)); 
     } 
    } 

的開關在其他幾項功能(的OnMouseMove,OnMouseUp)使用,這似乎是壞的設計(大開關複製幾個功能)。任何建議以更清潔和更可擴展的方式重構這樣的東西?我目前正在考慮擁有基類Tool類,並讓每個工具都有自己的類來覆蓋它使用的函數(OnMouseDown()等)。這聽起來合理嗎?

感謝您的閱讀。

回答

6

你有良好的直覺。

通常,在OOP中,當你有if或者humongous開關的行時,這是一種強烈的代碼異味。使這種氣味消失的最好方法是去與多態性。

你應該繼續你的想法,有一個基本的抽象類BaseTool,將不同的OnXXX方法實現爲nops(只是返回,所以如果你的工具知道如何在方法上作用,你只需要指定行爲) ,並讓每個工具都繼承自BaseTool並通過覆蓋相關方法來實現自己的行爲。

所以你的方法最終被

public void OnEditorViewMouseDown(Point mousePos) 
{ 
    currentTool.OnEditorViewMouseDown(mousePos); 
} 

根據你的設計,你也應該考慮通過檢測DragManager的方法,以免被捆綁到實例變量周圍鋪設。一個EditorContext(包含DragManager)符合該方法所需的所有內容,而無需獲取「全局」變量,這使得您的方法更具自包含性,並且在重構時更加脆弱。設計本身將取決於責任:誰負責什麼。

+2

是啊,這肯定是我在想什麼。這也被稱爲戰略模式,如rjohnston的回答所述 – zonkflut 2009-06-03 00:32:27

1

我對C#的語法並不熟悉,但總的來說,您可以使CurrentTool具有方法StartDragEndDrag,它接受DragManager作爲參數的對象。然後當鼠標在視圖上按下時,您只需調用CurrentTool.StartDrag(m_DragManager)

3

是的,你應該擁有一個基類(或至少是一個接口),它定義了所有工具中所需的所有常用方法。儘量讓這段代碼的工作,它會給你如何設計你的類是一個好主意:

m_DragManager.DragType = CurrentTool.DragType; 
m_DragManager.StartDrag(mousePos); 

其中「CurrentTool」是你的基類或你的界面的一個實例。所以基本上,當選擇一個「工具」時,在那一點上,你確定你正在處理的派生工具,但是從那時起,你只處理基類,忘記任何枚舉或任何東西像那樣來確定當前選擇的工具。合理?

-2

嘗試標記枚舉。每個位都是不同的功能,並且可以更好地堆疊每個單元的功能。

然後你可以用不同的方法檢查每一位。

+0

甜...倒票沒有評論。很高興在工作中看到創造性的批評。我的觀點是他的每個枚舉都有混合模式。如果標誌位標識每個模式,他可以用較少的代碼混合它們。這可以通過if語句或方法調用完成。但仍允許每個單元具有一個int或字節的相同類型的數據。但是如果您想要爲碰撞檢測和渲染提供複雜對象的開銷,請隨意。 – 2009-06-03 13:50:58

3

是的,polymorphism是你想要的。您應該定義abstract基類Tool或接口ITool,具體取決於您是否需要將實現添加到基類(即,如果在所有工具之間存在通用功能,則可以使用抽象基類)。

當需要完成某些事情時,您的經理應該拿一個工具或ITool。你的工具將實現一個Drag函數,該函數接收它需要的信息,並返回它需要返回的內容或做它需要做的事情。或者你可以在你的管理器和你的工具之間實現一個inversion of control,並通過屬性注入(Tool.Manager = dragManager)將管理器注入到工具中,讓工具完成使用管理器所需的工作。

5

聽起來就像使用策略模式的好去處:http://www.google.com/search?q=c%23+strategy+pattern

+1

這是策略模式解決的特定問題。我無法想象這個問題更好或者更適合使用這個模式。我強烈建議人們閱讀關於戰略模式的維基百科文章。特別需要注意的是,在支持頭等功能的語言中,您幾乎可以根本不需要代碼就可以實現策略模式。雖然C#技術上不支持第一類功能,但代表允許您將函數視爲第一類。 – 2009-06-03 01:15:54