我在c#中编程,你通过口述命令控制,所以现在我有一个很长的switch语句。像
这样的东西switch (command)
{
case "Show commands":
ProgramCommans.ShowAllCommands();
break;
case "Close window":
ControlCommands.CloseWindow();
break;
case "Switch window":
ControlCommands.SwitchWindow();
break;
}
等等
几乎所有情况都只调用一个方法,方法不在一个类中,而是分布在许多类中。所以问题是,我如何能够以更优雅的方式重构这种转变?
答案 0 :(得分: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();
}
这种方法的主要优点是你可以改变&#34;开关&#34;在运行时。
答案 1 :(得分:1)
如果所有函数都获得相同的参数并返回相同的值,则可以使用Dictionary和delegates将字符串映射到函数。此方法还允许您更改交换机的运行时间 - 允许外部程序扩展程序的功能。
如果函数不相同,你可以编写包装器 - 一个代理函数,它将获取所有其他函数的参数,并调用你想要的函数。
答案 2 :(得分:1)
我更喜欢使用策略模式来扩展切换案例语句。首先,我创建一个接口,定义每个规则的外观:
public interface IWindowRule
{
string Command { get; }
void Invoke();
}
然后创建一个实现每种可能情况的接口的类:
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();
}
然后您的switch语句变成这样:
public void RunWindowRule(IList<IWindowRule> rules, string command)
{
foreach (IWindowRule rule in rules)
{
if (rule.Command == command) rule.Invoke();
}
}
现在,您可以向函数传递所需的任何规则集并运行它们,以使函数遵循“打开/关闭”原则。
我意识到这似乎有点工程过度,而且我确实认为有更多的功能解决方案需要的工作量少一些,但是这样做还有一个额外的好处,即允许您通过创建注入的类来扩展此功能。各种情况下的规则列表,甚至制作出可以为您提供流畅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();
}
}
}
现在您有类似这样的内容:
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]);
}
这是高度可扩展的,因为对于任何新规则,您只需创建类并使用AddRule()方法将其添加到规则构建器中即可。不需要太多的阅读就可以了解这里发生的事情。这是一种更加组合的方法。尽管我再次承认,要实现它确实需要花费一些工作,但是代码遵循SOLID并已很好地分离。
答案 3 :(得分:0)
这是你在这里可以做的。您可以创建一个接口[ICommand],您可以在其中放置一个公共函数[例如:执行]。
然后您只需要使用适当的类型启动该成员并调用Execute函数。这可能包括未来的更多功能,因此得到了扩展。
此外,您可以创建一个工厂方法,您可以在其中传递参数并获取适当的类。
希望有所帮助。
答案 4 :(得分:0)
我意识到这是一个老帖子,但在这些情况下,我发现属性和工厂非常方便。
以下代码使用自定义属性(Command
)来允许您对方法进行属性化,并提供它们应如何响应您的字符串值。
工厂方法使用反射生成这些方法的字典,并在您调用CommandFactory
时调用它。
事情可能会被清理一下,调用invoke有点难看,但这取决于你想如何执行代码。
using System.Collections.Generic;
using System.Linq;
namespace MyApp
{
using System.Reflection;
using MyApp.Commands;
class Program
{
static void Main(string[] args)
{
var methods = new MyCommands();
MethodInfo myMethod;
myMethod = CommandFactory.GetCommandMethod("Show Commands");
myMethod.Invoke(methods, null);
myMethod = CommandFactory.GetCommandMethod("Close window");
myMethod.Invoke(methods, null);
myMethod = CommandFactory.GetCommandMethod("Switch window");
myMethod.Invoke(methods, null);
}
}
public static class CommandFactory
{
private static Dictionary<string, MethodInfo> speechMethods = new Dictionary<string, MethodInfo>();
public static MethodInfo GetCommandMethod(string commandText)
{
MethodInfo methodInfo;
var commands = new MyCommands();
if (speechMethods.Count == 0)
{
var methodNames =
typeof(MyCommands).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
var speechAttributeMethods = methodNames.Where(y => y.GetCustomAttributes().OfType<CommandAttribute>().Any());
foreach (var speechAttributeMethod in speechAttributeMethods)
{
foreach (var attribute in speechAttributeMethod.GetCustomAttributes(true))
{
speechMethods.Add(((CommandAttribute)attribute).Command, speechAttributeMethod);
}
}
methodInfo = speechMethods[commandText];
}
else
{
methodInfo = speechMethods[commandText];
}
return methodInfo;
}
}
}
namespace MyApp.Commands
{
class MyCommands
{
[Command("Show All")]
[Command("Show All Commands")]
[Command("Show commands")]
public void ShowAll()
{
ProgramCommands.ShowAllCommands();
}
[Command("Close Window")]
public void CloseWindow()
{
ControlCommands.CloseWindow();
}
[Command("Switch Window")]
public void SwitchWindow()
{
ControlCommands.SwitchWindow();
}
}
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = true)]
public class CommandAttribute : System.Attribute
{
public string Command
{
get;
set;
}
public CommandAttribute(string textValue)
{
this.Command = textValue;
}
}
}
答案 5 :(得分:0)
我知道答案有点晚,为了不滥用SOLID原则,你可以使用接口或继承。在这个例子中,我使用继承,因为你可能有其他用法的“命令”字符串。
public abstract class commandRepository {
string command ; // if there is no usage in other function class, you can get rid of it
public abstract void DoCommands();
}
public class ShowCommands:commandRepository
{
public ShowCommands (){
command ="Show commands"; // if there is no usage in other function class, you can get rid of it
}
public override void DoCommands(){
ProgramCommans.ShowAllCommands();
}
}
public class CloseWindow:commandRepository
{
public CloseWindow (){
command ="Close window"; // if there is no usage in other function class, you can get rid of it
}
public override void DoCommands(){
ProgramCommans.CloseWindow();
}
}
public class SwitchWindow:commandRepository
{
public SwitchWindow (){
command ="Switch window"; // if there is no usage in other function class, you can get rid of it
}
public override void DoCommands(){
ProgramCommans.SwitchWindow();
}
}