使用命令和工厂设计模式执行排队作业

我有一个在数据库中排队的作业列表,我需要从数据库中读取并使用线程并行执行它们,我有一个命令类列表来执行每个实现公共接口(命令模式)的作业。 但是当我从数据库中检索挂起的作业时,我需要为每个作业实例化正确的命令对象(在工厂类中)

ICommand command; switch (jobCode) { case "A": command = new CommandA(); break; case "B": command = new CommandB(); break; case "C": command = new CommandC(); break; } command.Execute(); 

有没有更好的方法来创建正确的命令对象而不使用上面的大开关语句? 或者是否有其他模式来执行排队的作业?

解决方案:这样解决(基于选定的答案)。 这将执行命令对象的延迟实例化。

 public class CommandFactory { private readonly IDictionary<string, Func> _commands; public CommandFactory() { _commands = new Dictionary<string, Func> { {"A", () => new CommandA()}, {"B", () => new CommandB()}, {"C", () => new CommandC()} }; } public ICommand GetCommand(string jobKey) { Func command; _commands.TryGetValue(jobKey.ToUpper(), out command); return command(); } } Client: var factory = new CommandFactory(); var command = factory.GetCommand(jobKey); command.Execute(); 

大多数C#命令模式实现与Java实现或多或少相同。 这些实现通常使用ICommand接口:

 public interface ICommand { void Execute(); } 

然后强制所有命令类实现接口。 我对这个解决方案没有任何问题,但我个人不喜欢创建太多的类而我更喜欢使用.NET委托(Java中没有委托)。 如果只需要一个方法引用,Action委托通常可以解决问题:

 public class Prog { public Prog() { var factory = new CommandFactory(); factory.Register("A", () => new A().DoA); factory.Register("B", () => new B().DoB); factory.Register("C", DoStuff); factory.Execute("A"); } public static void DoStuff() { } } public class CommandFactory { private readonly IDictionary _commands; public void Register(string commandName, Action action) { _commands.Add(commandName, action); } public Action GetCommand(string commandName) { _commands[commandName]; } public void Execute(string commandName) { GetCommand(commandName)(); } } public class A { public void DoA() { } } public class B { public void DoB() { } } 

如果您的命令界面需要多个方法,例如:

 public interface ICommand { void Execute(); void Undo(); } 

你可以使用这样的包装类:

 public class Command { public Command(Action execute, Action undo) { Execute = execute; Undo = undo; } public Action Execute { get; protected set; } public Action Undo { get; protected set; } } 

或者(哪一个没关系)

 public class Command { private readonly Action _execute; private readonly Action _undo; public Command(Action execute, Action undo) { _execute = execute; _undo = undo; } public void Execute() { _execute(); } public void Undo() { _undo(); } } 

(如果你已经有遗留的东西,这个甚至可以实现ICommand。如果你使用接口,工厂应该使用接口而不是Command类)

使用这样的包装器,您不必为要支持的每个操作创建命令类。 以下示例演示了如何使用包装类:

 public class Prog2 { public Prog2() { var factory = new CommandFactory2(); factory.Register("A", new Lazy( ()=> { var a = new A(); return new Command(a.DoA, a.UndoA); })); factory.Register("B", new Lazy( () => { var c = new B(); return new Command(c.DoB, c.DoB); })); factory.Register("C", new Lazy( () => new Command(DoStuff, UndoStuff))); factory.Execute("A"); } public static void DoStuff() { } public static void UndoStuff() { } } public class CommandFactory2 { private readonly IDictionary> _commands; public void Register(string commandName, Lazy lazyCommand) { _commands.Add(commandName, lazyCommand); } public void Register(string commandName, Action execute, Action undo) { _commands.Add(commandName, new Lazy(() => new Command(execute, undo))); } public Command GetCommand(string commandName) { return _commands[commandName].Value; } public void Execute(string commandName) { GetCommand(commandName).Execute(); } public void Undo(string commandName) { GetCommand(commandName).Undo(); } } public class A { public void DoA() { } public void UndoA() { } } public class B { public void DoB() { } public void UndoB() { } } 

正如您所看到的,即使您有多个方法(执行,撤消等),也无需实现该接口。 请注意,Execute和Undo方法可能属于不同的类。 您可以自由地构建代码,使其感觉更自然,并且仍然可以使用命令模式。

您可以使用Dictionary将字母/字符映射到相关的ICommand实现。 就像是:

 public class CommandFactory { private readonly Dictionary mCommands = new Dictionary(StringComparer.OrdinalIgnoreCase); public void RegisterCommand(string commandKey) where TCommand : ICommand, new() { // Instantiate the command ICommand command = new TCommand(); // Add to the collection mCommands.Add(commandKey, command); } public void ExecuteCommand(string commandKey) { // See if the command exists ICommand command; if (!mCommands.TryGetValue(commandKey, out command)) { // TODO: Handle invalid command key } // Execute the command command.Execute(); } } 

使用它,您可以注册命令类型并将它们映射到基于string的键,并允许它们更实际地实例化和执行。 您可以通过仅在首次使用时实例化命令类型来提高性能。

编辑

在回答您的评论时,仅在执行时进行实例化,您可以执行以下操作:

 public class CommandDetails where T : ICommand, new() { private ICommand mCommand; public ICommand GetCommand() { if (/* Determine if the command has been instantiated */) { // Instantiate the command mCommand = new T(); } return mCommand; } } public void ExecuteCommand(...) { // See if the command exists CommandDetails details; // ... // Get the command // Note: If we haven't got the command yet, this will instantiate it for us. ICommand command = details.GetCommand(); // ... } 

你可以考虑让你的工作提供它自己的ICommand:

 interface IJob { ICommand Command { get; } } public class JobA : IJob { private readonly ICommand _command = new CommandA(); public ICommand Command { get { return _command; } } } 

然后你可以这样做,而不是打开jobCode:

 job.Command.Execute();