在C#中重构大型交换机的建议
我在C#/ Winforms中有一个应用程序,它允许用户在网格上放置对象以创建游戏关卡。 它有几个工具用于放置瓷砖/灯/门/实体等。目前我只使用枚举来存储当前选定的工具,并有一个switch语句来运行每个工具代码。 随着我一直在为应用程序添加更多工具,它开始变得像意大利面一样,有很多重复的代码。
这是我的编辑器类中的鼠标按下function的缩减版本:
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()等)。 这听起来合情合理吗?
谢谢阅读。
你有很好的直觉。
通常,在OOP中,当你有一行if或者很多的开关时,它就会产生很强的代码味道。 使这种气味消失的最好方法是采用多态性。
您应该继续使用您的想法,使用基本抽象类BaseTool,将不同的OnXXX方法实现为nops(只返回,因此您只需指定行为,如果您的工具知道如何对该方法采取行动),并且每个工具inheritance自BaseTool并通过重写相关方法来实现自己的行为。
所以你的方法最终成为了
public void OnEditorViewMouseDown(Point mousePos) { currentTool.OnEditorViewMouseDown(mousePos); }
根据您的设计,您还应该考虑将DragManager传递给方法,以免与存在的实例变量绑定。 一个EditorContext(包含DragManager),无需获取“全局”变量就能满足方法所需的所有内容,这将使您的方法在重构时更加自包含且不那么脆弱。 设计本身将取决于责任:谁负责什么。
听起来像是使用策略模式的好地方: http : //www.google.com/search? q = c%23 + strategy +pattern
是的,你应该absolutley有一个基类(或至少是一个接口),它定义了所有工具所需的所有常用方法。 尝试使这段代码工作,它会让你很好地了解如何设计你的类:
m_DragManager.DragType = CurrentTool.DragType; m_DragManager.StartDrag(mousePos);
其中“CurrentTool”是基类或接口的实例。
所以基本上,当选择“工具”时,在那时你确定你正在处理哪个派生工具,但从那时起,你只处理基类,并忘记任何枚举或类似的东西来确定当前选择的工具。 合理?
是的, 多态性就是你想要的。 您应该定义抽象基类工具或接口ITool,具体取决于您是否需要向基础添加实现(即,如果所有工具之间存在共同function,则可以使用抽象基类)。
当需要完成某些事情时,您的经理应该使用工具或ITool。 您的工具将实现一个Drag函数,该函数获取所需的信息,并返回它需要返回的内容或执行它需要执行的操作。 或者,您可以在Manager和工具之间实现控制反转,并通过属性注入(Tool.Manager = dragManager)将Manager注入工具,然后让工具使用管理器执行它需要执行的操作。
我不太熟悉C#的语法,但一般来说,你可以使CurrentTool成为一个具有StartDrag
方法的对象, EndDrag
接受一个DragManager
作为参数。 然后,当在视图上按下鼠标时,您只需调用CurrentTool.StartDrag(m_DragManager)
。
尝试标记枚举。 每个位都是不同的特征,并且允许每个单元更好地堆叠特征。
然后你甚至可以用不同的方法检查每一位。