2009-06-02 18 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