2012-01-09 9 views
5

私はデータベースから読み込み、スレッドを使用して並列に実行する必要があるデータベースにキューされたジョブのリストを持っています。これらのジョブはすべて共通のインタフェース(コマンドパターン)を実装しています。しかし、私は、データベースからの保留中のジョブを取得するとき、私は各ジョブのために(ファクトリクラスで)このような何かを右のコマンドオブジェクトをインスタンス化する必要がありますキューに登録されたジョブを実行するためのコマンドとファクトリデザインパターンを使用して

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

command.Execute(); 

右のコマンドオブジェクトを作成するためのより良い方法はあります上記のような大きなswitch文を使用せずに?または、キューに登録されたジョブを実行するための他のパタ​​ーンはありますか?

解決策:このように解決しました(選択した回答に基づいて)。これは、コマンドオブジェクトの遅延インスタンス化を行います。

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(); 
+0

これはあなたのコマンドのすべてがあなたの工場になければならないことを考慮すると、間違っているようです。 – KingOfHypocrites

答えて

12

ほとんどのC#のコマンドパターンの実装:実行するとき

EDITあなたのコメントに答えて

、あなたのような何かができる、唯一のインスタンス化します。これらの実装は、通常、ICommandインターフェイスを使用します:

public interface ICommand 
{ 
    void Execute(); 
} 

そして、すべてのコマンドクラスが強制的にインターフェイスを実装します。私はこのソリューションに問題はありませんが、個人的にはあまりにも多くのクラスを作成するのが好きではなく、代わりに.NETデリゲートを使用する方が好みです(Javaではデリゲートはありません)。アクションデリゲートは、通常、1つの方法だけの参照が必要な場合は、トリックを行います。

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() 
    { 
    } 
} 

コマンド・インターフェースのような複数の方法を必要とする場合:

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; } 
} 

か(それは問題ではない1)

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(); 
    } 
} 

(これはすでにICEMandを実装しているかもしれません。インターフェイスを使用する場合、ファクトリは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() 
    { 
    } 
} 

あなたは複数のメソッド(実行、元に戻す、など)を持っている場合でも、インタフェースを実装する必要がない見ることができるように。 ExecuteメソッドとUndoメソッドは異なるクラスに属することがあることに注意してください。より自然な感じでコードを構造化することは自由ですが、依然としてコマンドパターンを使用することができます。

+0

あなたの提供された例はデザインの柔軟性と再利用性に欠けています。あなたの提供する例では、もっと似たハードコード化されたクラスを作成しています。 –

+0

デザインの柔軟性と再利用性に欠けていることを説明してください。委任署名を満たす任意のメソッドを辞書に追加できます。 「もっとハードコーディングされたクラス」の意味はどうですか? –

+0

factory.Register( "C" デザインに大きな欠陥があるハードコーディングの1つで、ハードコードしないでください – Zenwalker

1

あなたはそれが自分の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(); 
+1

これが役立つかどうかは不明です。コマンドオブジェクトの作成に切り替える代わりに、これを使って正しいジョブオブジェクトを作成するためのスイッチが必要です。 – RKP

+0

@RKPあなたの 'jobCode'はジョブから来ると仮定しました。ジョブがインスタンス化される時点で、あなたはどのタイプのジョブを望んでいるのか、それではどのコマンドも知っています。 –

+0

ここでもう一度、ICommandの代わりにIJobを調べる必要がある議論が行われます:) –

4

あなたが使用することができます文字/文字を関連するICommand実装にマップするためのDictionary。これを使用する

public class CommandFactory 
{ 
    private readonly Dictionary<string, ICommand> mCommands = new Dictionary<string,ICommand>(StringComparer.OrdinalIgnoreCase); 

    public void RegisterCommand<TCommand>(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ベースのキーにマッピングし、それらをインスタンス化し、一般的に多くを実行することを可能にすることができますような何か。コマンドタイプを最初に使用するときにインスタンス化するだけで、パフォーマンスを向上させることができます。 Java実装として、多かれ少なかれ同じ

public class CommandDetails<T> 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(); 

    // ... 
} 
+0

ありがとうございます。これはコマンドパターンの古典的な例です。すべてのコマンドオブジェクトをコンテナクラスのコレクションに格納しますが、これを使用してインスタンス化するにはどのように改善できますか? – RKP

+0

@RKP私の拡張回答を参照してください。 –

+0

しかし、私はどのようにそれをインスタンス化せずに最初の場所で(辞書にコマンドを追加する)コマンドを登録することができますか? 1つのオプションは、別の回答で指定された代理人を使用することです。それ以上の解決策があるかどうかを知ることに興味があります。 – RKP