2010-10-11 7 views
7

¿Es posible implementar el patrón de comando GOF usando un delegado de Queue of Action?Implementando el Patrón de Comando usando el delegado de Acción C#

He estado tratando de entenderlo por un tiempo y estoy perplejo porque cada una de las posibles acciones que deseo agregar a la cola puede tener un número variado de parámetros.

¿Alguna sugerencia? ¿Estoy ladrando al árbol equivocado al enfocarme en el patrón de comando?

ACTUALIZACIÓN:

Muchas gracias jgauffin, funciona un convite ... mi aplicación ahora se ve como

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

Respuesta

10

Puede usar una Acción. No deberías usar múltiples parámetros. ¿Qué sucede si un comando necesita un nuevo parámetro? Entonces necesitarías cambiar todos los lugares invocando el comando más el controlador.

En su lugar, debe utilizar las clases de comando que tiene todos los parámetros como propiedades. De esta forma, puede agregar parámetros sin que afecte el código (los nuevos parámetros deben tratarse como opcionales en el controlador).

esta es la forma en que lo haría:

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

Tenga en cuenta que el código es una especie de pseudo-código. Lo escribí directamente en la respuesta sin probarlo. Probablemente tengas que cambiar cosas para que funcione, pero al menos debería darte una pista. La implementación le permite agregar múltiples suscriptores para cada comando.

+0

Esto parece una solución interesante, ¿te importaría explicar cómo funcionaría el método de activación? comando (T) o comando.Invocar()? ¿Y por qué un diccionario de comandos, por qué no solo una lista o cola? – Dve

+0

El diccionario es más rápido, no tiene que recorrer la colección usted mismo para encontrar el tipo de comando correcto (y sus suscriptores) – jgauffin

+0

He agregado implementaciones. Es posible que no funcionen al 100%. Intenta hacerlo funcionar. – jgauffin

1

Si está preocupado con el número de parámetros, a continuación, aplicar adecuadamente el patrón de comandos usar una clase sería el camino correcto a seguir. El delegado Action está limitado a solo uno. Además, si usa el delegado Action, es posible que desee implementar un Deshacer más adelante, que no podrá realizar ya que solo utilizó un delegado en lugar de una clase.

3

En el patrón de comando, la interfaz de comando típica tendría un método de ejecución simple; esto puede representarse mediante un delegado de Acción. Pero la implementación real será proporcionada por diferentes clases concretas donde podrá/puede pasar parámetros (por ejemplo, a través del constructor). Por ejemplo:

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

     ... 
    } 


} 

El punto aquí es que el estado de comandos relacionados e implantación se pueden encapsular dentro de una implementación diferente, mientras que Acción delegado apuntará a su método de instancia. Por lo tanto, invocar al delegado dará como resultado la ejecución del comando.

Cuestiones relacionadas