7 Stimmen

Möchtegern-Befehlsobjektmuster oder das echte?

Das Muster des Befehlsobjekts ist eines, das ich immer noch nicht richtig begreifen konnte, und ich habe eine Implementierung in dem Code gefunden, an dem ich gerade arbeite, also habe ich es lange und intensiv studiert, um zu sehen, ob ich es endlich mit einem Beispiel aus der realen Welt verstehen kann. Das Problem ist, dass ich mir sicher bin, dass es nicht richtig implementiert ist und dass es nur ein Versuch von jemandem ist, der gerade darüber gelesen hat und dachte, dass es hier Sinn macht.

Erlauben Sie mir, es Ihnen zu zeigen (aus Gründen der Vertraulichkeit wird es stark vereinfacht sein, aber ich werde mein Bestes tun, um die wichtigsten Konzepte zu zeigen):

public class CommandOne
{
   public CommandOne(Manager manager, MyForm form)
   {
      m_manager = manager;
      m_form = form;
   }

   public void Execute()
   {
       m_manager.CommandOne(m_form);
   }
}

public class CommandTwo
{
   public CommandTwo(Manager manager, MyForm form)
   {
      m_manager = manager;
      m_form = form;
   }

   public void Execute()
   {
       m_manager.CommandTwo(m_form);
   }
}

Das erste, was mir auffällt, ist, dass diese beiden Klassen weder von einer abstrakten Klasse erben noch eine gemeinsame Schnittstelle implementieren.

Der Code, der diese Befehle verwendet, lautet wie folgt:

public class MyForm : System.Windows.Forms.Form
{
   public MyForm(Manager manager)
   {
      m_manager = manager;
   }

   private void SomeMethod()
   {
      ....
      var cmd = new CommandOne(manager, this);
      cmd.Execute();
      ...
   }

   private void OtherMethod()
   {
      ....
      var cmd = new CommandTwo(manager, this);
      cmd.Execute();
      ...
   }
}

So wie ich das sehe, ist dieses Formular absolut an alle beteiligten Klassen gekoppelt, mit Ausnahme des Managers, der über seine Konstruktoren in das Formular injiziert wird. Mit diesem Code sehe ich also wirklich keinen Vorteil darin, die "Befehlsklassen" zu erstellen, die im Grunde nur den Aufruf der Methoden des Managers delegieren, da das Formular sie instanziiert, wenn es sie braucht, und die Ausführungsmethode direkt danach aufruft.

Könnte also bitte jemand erklären, welche Teile, wenn überhaupt, dieser Implementierung fehlen, um wirklich ein Befehlsobjektmuster zu sein, und, obwohl es vielleicht zu subjektiv ist, was wäre der Vorteil, es in diesem Fall zu implementieren?

Ich danke Ihnen.

12voto

Mike Valenty Punkte 8755

Ausgehend von dem, was Sie hier zeigen, sieht es so aus, als ob der Nutzen des Befehlsmusters verloren gegangen ist. Es gibt ein paar Gründe, warum Sie das Befehlsmuster im Kontext einer WinForms-Anwendung verwenden möchten.

Sie möchten einen Befehl später ausführen

public interface ICommand
{
    void Execute();
}

Aufzeichnung der ausgeführten Befehle, damit sie vom Benutzer rückgängig gemacht werden können

public interface ICommand
{
    void Execute();

    void Undo();
}

Überprüfen Sie die Berechtigungen, um festzustellen, ob der aktuelle Benutzer das Recht hat, den Befehl auszuführen. Zum Beispiel haben Sie vielleicht einen RefundCustomerCommand und nicht alle Kundendienstmitarbeiter das Recht haben, eine Erstattung auszustellen, so dass Sie eine Schaltfläche im Formular deaktivieren möchten.

public interface ICommand
{
    void Execute();

    bool CanExecute { get; }
}

Sie können auch mehrere Befehle zu einem Kompositum zusammenfassen, etwa so:

public class CompositeCommand : ICommand
{
    private readonly List<ICommand> commands;

    public CompositeCommand()
    {
        commands = new List<ICommand>();
    }

    public void Add(ICommand command)
    {
        commands.Add(command);
    }

    public void Execute()
    {
        foreach (var command in commands) command.Execute();
    }
}

Das Befehlsmuster funktioniert auch sehr gut mit dem Dekorator. Sie können Ihren Befehlen leicht zusätzliches übergreifendes Verhalten wie Wiederholungslogik hinzufügen:

public class RetryOnTimeout : ICommand
{
    private readonly ICommand command;
    private int numberOfRetries;

    public RetryOnTimeout(ICommand command, int numberOfRetries)
    {
        this.command = command;
        this.numberOfRetries = numberOfRetries;
    }

    public void Execute()
    {
        try
        {
            command.Execute();
        }
        catch (TimeoutException)
        {
            if (++numberOfRetries > 3)
                throw;

            Execute();
        }
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X