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

RKP*_*RKP 6 c# design-patterns command-pattern factory-pattern

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

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

command.Execute();
Run Code Online (Sandbox Code Playgroud)

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

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

public class CommandFactory
{
    private readonly IDictionary<string, Func<ICommand>> _commands;

    public CommandFactory()
    {
        _commands = new Dictionary<string, Func<ICommand>>
                        {
                            {"A", () => new CommandA()},
                            {"B", () => new CommandB()},
                            {"C", () => new CommandC()}
                        };
    }

    public ICommand GetCommand(string jobKey)
    {
        Func<ICommand> command;
        _commands.TryGetValue(jobKey.ToUpper(), out command);
        return command();
    }
}    

Client: 

        var factory = new CommandFactory();
        var command = factory.GetCommand(jobKey);
        command.Execute();
Run Code Online (Sandbox Code Playgroud)

Jen*_*zlo 13

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

public interface ICommand
{
    void Execute();
}
Run Code Online (Sandbox Code Playgroud)

然后强制所有命令类实现接口.我对这个解决方案没有任何问题,但我个人不喜欢创建太多的类而我更喜欢使用.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<string, Action> _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()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public interface ICommand
{
    void Execute();
    void Undo();
}
Run Code Online (Sandbox Code Playgroud)

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

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; }
}
Run Code Online (Sandbox Code Playgroud)

或者(哪一个没关系)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

public class Prog2
{
    public Prog2()
    {
        var factory = new CommandFactory2();
        factory.Register("A", new Lazy<Command>(
            ()=>
                {
                    var a = new A();
                    return new Command(a.DoA, a.UndoA);
                }));

        factory.Register("B", new Lazy<Command>(
           () =>
           {
               var c = new B();
               return new Command(c.DoB, c.DoB);
           }));

        factory.Register("C", new Lazy<Command>(
            () => new Command(DoStuff, UndoStuff)));

        factory.Execute("A");
    }

    public static void DoStuff()
    {
    }

    public static void UndoStuff()
    {
    }
}

public class CommandFactory2
{
    private readonly IDictionary<string, Lazy<Command>> _commands;

    public void Register(string commandName, Lazy<Command> lazyCommand)
    {
        _commands.Add(commandName, lazyCommand);
    }

    public void Register(string commandName, Action execute, Action undo)
    {
        _commands.Add(commandName, new Lazy<Command>(() => 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()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我甚至不理解Slade的例子你必须指定像factory.RegisterCommand <SomeCommand>("C")这样的键.让我指出我的示例允许不会强制您创建实例,另外您也可以注册非空构造函数类的方法. (2认同)