Frage

Ist es möglich, den GOF Befehl Muster unter Verwendung einer Warteschlange Aktions Delegierten implementieren?

Ich habe versucht, meinen Kopf zu wickeln um es für eine Weile und ich bin ratlos, weil jede der möglichen Aktionen ich in die Warteschlange hinzufügen möchten eine varing Anzahl von Parametern haben.

Irgendwelche Vorschläge? Bin ich Bellen durch die Konzentration auf das Befehlsmuster der falschen Baum?

UPDATE:

Vielen Dank jgauffin, es funktioniert ein Genuss ... meine Implementierung sieht nun wie

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;
        }
    }
}
War es hilfreich?

Lösung

können Sie eine Aktion verwenden. Sie sollten nicht mehrere Parameter verwenden. Was passiert, wenn ein Befehl einen neuen Parameter benötigt? Dann müssen Sie alle Orte ändern Sie den Befehl und die Handler aufgerufen wird.

Sie sollten stattdessen Befehlsklassen verwendet werden, die alle Parameter als Eigenschaften aufweist. Auf diese Weise können Sie Parameter hinzufügen, ohne den Code zu beeinflussen (die neuen Parameter sollten als optional in der Prozedur behandelt werden).

Dies ist, wie ich es tun würde:

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

Beachten Sie, dass der Code Art von Pseudo-Code ist. Ich habe es direkt ohne vorher zu testen in der Antwort geschrieben. Sie werden wahrscheinlich zu ändern Sachen haben, um zu bekommen es funktioniert, aber es sollte Ihnen einen Hinweis zumindest geben. Die Umsetzung lässt Sie mehrere Teilnehmer für jeden Befehl.

hinzufügen

Andere Tipps

In Befehlsmustern, typische Kommandoschnittstelle würde einfache Methode ausführen - dies kann durch Aktion Delegierten vertreten. Aber tatsächliche Umsetzung wird durch verschiedene konkrete Klassen zur Verfügung gestellt werden, wo Sie / können Parameter übergeben (zum Beispiel über Konstruktor). Zum Beispiel:

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

       ...
   }


}

Der Punkt hier ist, dass Befehl bezogene Zustand und Implementierung würde innerhalb verschiedener Implementierung eingekapselt werden, während Aktionsdelegate seiner Instanz-Methode verweisen. So die Delegierten Aufruf in der Ausführung des Befehls führen wird.

Wenn Sie mit der Anzahl der betroffenen Parameter sind, dann Umsetzung ordnungsgemäß den Befehl Muster eine Klasse unter Verwendung wäre der richtige Weg zu gehen. Der Action Delegat wird auf nur eine beschränkt. Auch, wenn Sie die Action Delegat verwenden, könnten Sie eine Undo später, auf dem implementieren möchten Sie nicht in der Lage sein zu tun, da Sie nur einen Delegierten statt einer Klasse verwendet werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top