重构长切换语句

use*_*672 8 .net c# refactoring switch-statement

我在c#中编程,你通过口述命令控制,所以现在我有一个很长的switch语句.就像是

switch (command)

{
    case "Show commands":
        ProgramCommans.ShowAllCommands();
        break;
    case "Close window":
        ControlCommands.CloseWindow();
        break;
    case "Switch window":
        ControlCommands.SwitchWindow();
        break;
}
Run Code Online (Sandbox Code Playgroud)

等等

几乎所有情况都只调用一个方法,方法不在一个类中,它们分布在许多类中.所以问题是,我如何能够将这种转换重构为更优雅的方式?

Eni*_*ity 8

您可以这样做来重构您的switch语句:

var commands = new Dictionary<string, Action>()
{
    { "Show commands", () => ProgramCommans.ShowAllCommands() },
    { "Close window", () => ControlCommands.CloseWindow() },
    { "Switch window", () => ControlCommands.SwitchWindow() },
};

if (commands.ContainsKey(command))
{
    commands[command].Invoke();
}
Run Code Online (Sandbox Code Playgroud)

这种方法的主要优点是您可以在运行时更改"切换".


Xip*_*ooo 8

我更喜欢使用策略模式来扩展 switch case 语句。首先,我创建一个接口来定义每个规则的外观:

public interface IWindowRule 
{
    string Command { get; }
    void Invoke();
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个类来实现每种可能情况的接口:

public class ShowAllWindowRule : IWindowRule
{
    public string Command => "Show commands";
    private ProgramCommands _progCommands;

    public ShowAllWindowRule(ProgramCommands programCommands) =>
          _progCommands = programCommands;

    public void Invoke() => _progCommands.ShowAllCommands();
}

public class CloseWindowRule : IWindowRule
{
    private ControlCommands _ctrlCommands;
    public string Command => "Close window";

    public CloseWindowRule(ControlCommands ctrlCommands) =>
        _ctrlCommands = ctrlCommands;

    public void Invoke() =>
        _ctrlCommands.CloseWindow();
}

public class SwitchWindowRule : IWindowRule
{
    private ControlCommands _ctrlCommands;
    public string Command => "Switch window";

    public SwitchWindowRule(ControlCommands ctrlCommands) =>
        _ctrlCommands = ctrlCommands;

    public void Invoke() =>
        _ctrlCommands.SwitchWindow();
}
Run Code Online (Sandbox Code Playgroud)

然后你的 switch 语句变成这样:

public void RunWindowRule(IList<IWindowRule> rules, string command)
{
    foreach (IWindowRule rule in rules)
    {
        if (rule.Command == command) rule.Invoke();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以向函数传递任何您希望的规则集并运行它们,使函数遵循开放/关闭原则。

我意识到这可能看起来有点过度工程,而且我确实认为有更多功能解决方案需要更少的工作,但是这还有一个额外的好处,即允许您通过创建注入列表的类来扩展此功能为各种情况制定规则,甚至创建一个为您提供流畅 API 的构建器类。

public class WindowRuleBuilder
{
    private IList<IWindowRule> rules;
    public WindowRuleBuilder(IList<IWindowRule> rules = null) =>
        rules = rules ?? new List<IWindowRule>();

    public WindowRuleBuilder AddRule(IWindowRule newRule)
    {
        rules.Add(newRule);
        return this;
    }
    public void Run(string command)
    {
        foreach (IWindowRule rule in rules)
        {
            if (rule.Command == command) rule.Invoke();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你有这样的东西:

public static void Main(string[] args)
{
    WindowRuleBuilder ruleBuilder = new WindowRuleBuilder()
        .AddRule(new CloseWindowRule(conrolCommands))
        .AddRule(new ShowAllWindowRule(programCommands))
        .AddRule(new SwitchWindowRule(controlCommands));
    ruleBuilder.Run(args[0]);
}
Run Code Online (Sandbox Code Playgroud)

这是高度可扩展的,因为对于新规则,您只需创建该类并使用 AddRule() 方法将其添加到规则构建器中。也不需要读太多书就能理解这里发生的事情。这是一种更具组合性的方法。尽管我再次承认,实现它确实需要一些工作,但代码遵循 SOLID 并进行了很好的解耦。


ely*_*hiv 2

如果所有函数都获取相同的参数并返回相同的值,则可以使用字典委托字符串映射到函数。此方法还允许您在运行时更改开关 - 允许外部程序扩展程序的功能。

如果函数不相同,您可以编写包装器 - 一个代理函数,它将像所有其他函数一样获取参数,并调用您想要的函数。