7

アクション デリゲートのキューを使用して GOF コマンド パターンを実装することは可能ですか?

しばらくの間、頭を抱えようとしてきましたが、キューに追加したい可能なアクションのそれぞれにさまざまな数のパラメーターがある可能性があるため、困惑しています。

助言がありますか?コマンド パターンに注目して間違ったツリーを鳴らしていませんか?

アップデート:

jgauffinに感謝します。うまくいきます...私の実装は次のようになります

public class CommandDispatcher
{
    private readonly Dictionary<Type, List<Action<ICommand>>> _registeredCommands =
        new Dictionary<Type, List<Action<ICommand>>>();

    public void RegisterCommand<T>(Action<ICommand> action) where T : ICommand
    {
        if (_registeredCommands.ContainsKey(typeof (T)))
            _registeredCommands[typeof (T)].Add(action);
        else
            _registeredCommands.Add(typeof (T), new List<Action<ICommand>> {action});
    }

    public void Trigger<T>(T command) where T : ICommand
    {
        if (!_registeredCommands.ContainsKey(typeof(T)))
            throw new InvalidOperationException("There are no subscribers for that command");

        foreach (var registeredCommand in _registeredCommands[typeof(T)])
        {
            registeredCommand(command);
            if (command.Cancel) break;
        }
    }
}
4

3 に答える 3

10

アクションを使用できます。複数のパラメーターを使用しないでください。コマンドに新しいパラメータが必要な場合はどうなりますか?次に、コマンドとハンドラーを呼び出すすべての場所を変更する必要があります。

代わりに、すべてのパラメーターをプロパティとして持つCommandクラスを使用する必要があります。このようにして、コードに影響を与えることなくパラメーターを追加できます(新しいパラメーターはハンドラーでオプションとして扱われる必要があります)。

これは私がそれをする方法です:

public interface ICommand
{
    // Cancel processing, do not invoke any more handlers
    public bool Cancel { get; set; }
}

public class CommandDispatcher 
{
  private Dictionary<Type, List<Action<ICommand>>> _commands = new Dictionary<Type, List<Action<ICommand>>>();


  // Add to dictionary here
  public void Subscribe<T>(Action<T> action) where T : ICommand
  {
      List<Action<ICommand>> subscribers;
      if (!_commands.TryGetValue(typeof(T), out subscribers))
      {
          subscribers = new List<Action<ICommand>>();
          _commands.Add(typeof(T), subscribers));
      }

      subscribers.Add(action);
  }

  // find command and to foreach to execute the actions      
  public void Trigger<T>(T command) where T : ICommand
  {
      List<Action<ICommand>> subscribers;
      if (!_commands.TryGetValue(typeof(T), out subscribers))
          throw new InvalidOperationException("There are no subscribers for that command");

      foreach(var subsriber in subscribers)
      {
          subscriber(command);
          if (command.Cancel)
              break; //a handler canceled the command to prevent others from processing it.
      }
  }

}

public class AddTextCommand : ICommand
{
    public string TextToAdd {get;set;}
}

public class TextHandler
{
    public TextHandler(CommandDispatcher dispatcher)
    {
        disptacher.Subscribe<AddTextCommand>(OnAddText);
    }

    public void OnAddText(AddTextCommand cmd)
    {
        //....
    }
}


public partial class MyForm : Form
{
    CommandDispatcher _dispatcher;

    private void MyTextBox_Changed(object source, EventArgs e)
    {
        _dispatcher.Trigger(new AddTextCommand{TextToAdd = MyTextBox.Text}=;
    } 
}

コードは一種の擬似コードであることに注意してください。私はそれをテストせずに答えに直接書いた。おそらくそれを機能させるために何かを変更する必要があるでしょうが、それは少なくともあなたにヒントを与えるはずです。実装では、コマンドごとに複数のサブスクライバーを追加できます。

于 2010-10-11T06:15:32.080 に答える
3

コマンド パターンでは、典型的なコマンド インターフェイスには単純な実行メソッドがあり、これはアクション デリゲートで表すことができます。ただし、実際の実装は、パラメーターを渡す/渡すことができるさまざまな具象クラスによって提供されます (たとえば、コンストラクターを介して)。例えば:

public interface ICommand 
{
   public void Execute();
}

public class Command1 : ICommand
{
   public Command1(int param1, string param2)
   {
   }

   ...
}

public class Command2 : ICommand
{
  ...
}

public class Program
{

   public static void Main()
   {

       ...

       var commands = new List<Action>();
       commands.Add((new Command1(3, "Hello")).Execute);
       commands.Add((new Command2(...)).Execute);

       ...
   }


}

ここでのポイントは、コマンド関連の状態と実装が異なる実装内にカプセル化され、アクション デリゲートがそのインスタンス メソッドを指すことです。したがって、デリゲートを呼び出すと、コマンドが実行されます。

于 2010-10-11T06:23:30.047 に答える
1

パラメーターの数が気になる場合は、クラスを使用してコマンド パターンを適切に実装するのが正しい方法です。代理人は1Action人に限定されます。また、Actionデリゲートを使用する場合、クラスの代わりにデリゲートを使用したため、後で実行できない元に戻すを実装することもできます。

于 2010-10-11T05:56:34.043 に答える