在C#中重构大型交换机的建议

时间:2009-06-02 23:55:35

标签: c# switch-statement

我在C#/ Winforms中有一个应用程序,它允许用户在网格上放置对象以创建游戏关卡。它有几个工具用于放置瓷砖/灯/门/实体等。目前我只使用枚举来存储当前选定的工具,并有一个switch语句来运行每个工具代码。正如我一直在为应用程序添加更多工具,它开始变得像意大利面一样,有很多重复的代码。

以下是编辑器类中鼠标按下功能的缩减版本:

    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()等)。这听起来合理吗?

感谢阅读。

7 个答案:

答案 0 :(得分:6)

你有很好的直觉。

通常情况下,在OOP中,如果你有一行if或者很多的开关,那就是强烈的代码味道。使这种气味消失的最好方法是采用多态性。

你应该继续你的想法,有一个基本抽象类BaseTool,使用不同的OnXXX方法实现为nops(只返回,所以你只需要指定行为,如果你的工具知道如何对方法采取行动),并让每个工具继承BaseTool并通过覆盖相关方法来实现自己的行为。

所以你的方法最终成为了

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

根据您的设计,您还应该考虑将DragManager传递给方法,以免与存在的实例变量绑定。一个EditorContext(包含DragManager),无需获取“全局”变量就可以满足方法所需的所有内容,这将使您的方法在重构时更加自包含且不那么脆弱。设计本身将取决于责任:谁负责什么。

答案 1 :(得分:5)

听起来像是使用策略模式的好地方:http://www.google.com/search?q=c%23+strategy+pattern

答案 2 :(得分:3)

是的,你应该absolutley有一个基类(或至少是一个接口),它定义了所有工具所需的所有常用方法。尝试使这个代码工作,它会让你很好地了解如何设计你的类:

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

其中“CurrentTool”是基类或接口的实例。

所以基本上,当选择“工具”时,你可以确定你正在处理哪个派生工具,但从那时起,你只处理基类,忘记任何枚举或类似的东西那是为了确定当前选择的工具。有意义吗?

答案 3 :(得分:3)

是的,polymorphism就是你想要的。您应该定义abstract基类工具或接口ITool,具体取决于您是否需要向基础添加实现(即,如果所有工具之间存在共同功能,则可以使用抽象基类)。

当需要完成某些事情时,您的经理应该使用工具或ITool。您的工具将实现一个Drag函数,该函数获取所需的信息并返回它需要返回的内容或执行它需要执行的操作。或者您可以在管理器和工具之间实现inversion of control,并通过属性注入(Tool.Manager = dragManager)将管理器注入工具,然后让工具使用管理器执行它需要做的事情。

答案 4 :(得分:2)

答案 5 :(得分:1)

我对C#的语法并不十分熟悉,但一般来说,你可以使CurrentTool成为一个方法StartDragEndDrag接受DragManager作为参数的对象。然后,当您在视图上按下鼠标时,只需调用CurrentTool.StartDrag(m_DragManager)

答案 6 :(得分:-2)

尝试标记枚举。每个位都是不同的功能,可以更好地堆叠每个单元的功能。

然后你甚至可以用不同的方法检查每一位。