delphi - march for our lives petition




Warum ist es schlecht, einen Eventhandler aus dem Code aufzurufen? (6)

Angenommen, Sie haben einen Menüeintrag und eine Schaltfläche, die dieselbe Aufgabe ausführen. Warum ist es eine schlechte Übung, den Code für die Aufgabe in das Aktionsereignis eines Steuerelements zu schreiben und dann von dem anderen Steuerelement einen Aufruf an dieses Ereignis zu senden? Delphi erlaubt dies genauso wie vb6 aber realbasic nicht und sagt, dass Sie den Code in eine Methode schreiben sollten, die dann sowohl über das Menü als auch über die Schaltfläche aufgerufen wird


Angenommen, Sie entscheiden irgendwann, dass der Menüpunkt nicht mehr sinnvoll ist und Sie den Menüpunkt loswerden wollen. Wenn Sie nur über ein anderes Steuerelement verfügen, das auf den Ereignishandler des Menüelements zeigt, ist das möglicherweise kein großes Problem. Sie können den Code einfach in den Ereignishandler der Schaltfläche kopieren. Aber wenn Sie mehrere Möglichkeiten haben, den Code aufzurufen, müssen Sie viele Änderungen vornehmen.

Persönlich mag ich die Art und Weise, wie Qt damit umgeht. Es gibt eine QAction-Klasse mit einem eigenen Event-Handler, der süchtig gemacht werden kann. Dann ist die QAction allen Benutzeroberflächenelementen zugeordnet, die diese Aufgabe ausführen müssen.


Angenommen, Sie haben irgendwann entschieden, dass das Menü etwas anders machen sollte. Vielleicht geschieht diese neue Veränderung nur unter bestimmten Umständen. Sie vergessen den Knopf, aber jetzt haben Sie auch sein Verhalten geändert.

Auf der anderen Seite, wenn Sie eine Funktion aufrufen, werden Sie weniger wahrscheinlich ändern, was es tut, da Sie (oder der nächste Typ) weiß, dass dies schlimme Folgen haben wird.


Ein weiterer wichtiger Grund ist die Testbarkeit. Wenn der Ereignisbehandlungscode in der Benutzeroberfläche verborgen ist, besteht die einzige Möglichkeit, dies zu testen, entweder durch manuelles Testen oder automatisches Testen, das stark an die Benutzeroberfläche gebunden ist. (zB Menü A öffnen, Taste B drücken). Jede Änderung in der Benutzeroberfläche kann dann natürlich Dutzende von Tests unterbrechen.

Wenn der Code in ein Modul umstrukturiert wird, das sich ausschließlich mit dem zu erfüllenden Job beschäftigt, wird das Testen viel einfacher.


Es ist eine Frage, wie Ihr Programm organisiert ist. In dem von Ihnen beschriebenen Szenario wird das Verhalten des Menüelements in Bezug auf die Schaltflächen definiert:

procedure TJbForm.MenuItem1Click(Sender: TObject);
begin
  // Three different ways to write this, with subtly different
  // ways to interpret it:

  Button1Click(Sender);
  // 1. "Call some other function. The name suggests it's the
  //    function that also handles button clicks."

  Button1.OnClick(Sender);
  // 2. "Call whatever method we call when the button gets clicked."
  //    (And hope the property isn't nil!)

  Button1.Click;
  // 3. "Pretend the button was clicked."
end;

Jede dieser drei Implementierungen funktioniert, aber warum sollte der Menüpunkt so abhängig von der Schaltfläche sein? Was ist das Besondere an dem Button, dass er den Menüpunkt definieren soll? Wenn ein neues UI-Design keine Schaltflächen mehr enthält, was würde dann mit dem Menü passieren? Ein besserer Weg besteht darin, die Aktionen des Event-Handlers zu berücksichtigen, damit er unabhängig von den Steuerelementen ist, an die er angehängt ist. Dafür gibt es mehrere Möglichkeiten:

  1. Eine besteht darin, die MenuItem1Click Methode vollständig zu Button1Click und der MenuItem1.OnClick Ereigniseigenschaft die Button1Click Methode MenuItem1.OnClick . Es ist verwirrend, Methoden für Schaltflächen zu haben, die den Ereignissen von Menüelementen zugeordnet sind. Daher sollten Sie den Ereignishandler umbenennen, aber das ist in Ordnung, denn im Gegensatz zu VB definieren die Methodennamen von Delphi nicht , welche Ereignisse sie behandeln. Sie können jedem Ereignishandler eine beliebige Methode zuweisen, solange die Signaturen übereinstimmen. Die OnClick Ereignisse beider Komponenten sind vom Typ TNotifyEvent , sodass sie sich eine einzelne Implementierung teilen können. Nenne Methoden für das, was sie tun, nicht was ihnen gehört.

  2. Eine andere Möglichkeit besteht darin, den Event-Handler-Code der Schaltfläche in eine separate Methode zu verschieben und diese Methode dann von den Ereignisbehandlungsroutinen beider Komponenten aus aufzurufen:

    procedure HandleClick;
    begin
      // Do something.
    end;
    
    procedure TJbForm.Button1Click(Sender: TObject);
    begin
      HandleClick;
    end;
    
    procedure TJbForm.MenuItem1Click(Sender: TObject);
    begin
      HandleClick;
    end;
    

    Auf diese Weise ist der Code, der wirklich funktioniert, nicht direkt an eine der beiden Komponenten gebunden. Dies gibt Ihnen die Freiheit, diese Steuerelemente einfacher zu ändern , z. B. durch Umbenennen oder durch andere Steuerelemente. Die Trennung des Codes von der Komponente führt uns zum dritten Weg:

  3. Die TAction Komponente, die in Delphi 4 eingeführt wurde, wurde speziell für die von Ihnen beschriebene Situation entwickelt, bei der mehrere UI-Pfade zu demselben Befehl vorhanden sind. (Andere Sprachen und Entwicklungsumgebungen stellen ähnliche Konzepte bereit; Delphi ist nicht eindeutig.) TAction Sie Ihren Ereignisbehandlungscode in den TAction -Ereignishandler der TAction und weisen Sie diese Aktion dann der Action Eigenschaft der Schaltfläche und des Menüelements zu.

    procedure TJbForm.Action1Click(Sender: TObject);
    begin
      // Do something
      // (Depending on how closely this event's behavior is tied to
      // manipulating the rest of the UI controls, it might make
      // sense to keep the HandleClick function I mentioned above.)
    end;
    

    Möchten Sie ein weiteres UI-Element hinzufügen, das wie die Schaltfläche funktioniert? Kein Problem. Fügen Sie es hinzu, legen Sie die Action Eigenschaft fest, und Sie sind fertig. Sie müssen nicht mehr Code schreiben, damit das neue Steuerelement wie das alte aussieht und sich verhält. Sie haben diesen Code bereits einmal geschrieben.

    TAction geht über nur Event-Handler hinaus. Dadurch können Sie sicherstellen, dass Ihre UI-Steuerelemente über einheitliche Eigenschaften verfügen , darunter Beschriftungen, Hinweise, Sichtbarkeit, Aktivierung und Symbole. Wenn ein Befehl zu diesem Zeitpunkt nicht gültig ist, legen Sie die Enabled Eigenschaft der Aktion entsprechend fest, und alle verknüpften Steuerelemente werden automatisch deaktiviert. Sie müssen sich keine Sorgen machen, dass ein Befehl über die Symbolleiste deaktiviert wird, aber beispielsweise über das Menü aktiviert bleibt. Sie können sogar das OnUpdate Ereignis der Aktion verwenden, damit sich die Aktion basierend auf den aktuellen Bedingungen aktualisieren kann, anstatt dass Sie immer wissen müssen, wenn etwas passiert, bei dem Sie möglicherweise sofort die Eigenschaft Enabled festlegen müssen.


Warum ist es schlecht? Weil es viel einfacher ist, Code wiederzuverwenden, wenn er nicht in UI-Steuerelemente eingebettet ist.

Warum können Sie es nicht in REALbasic tun? Ich bezweifle, dass es irgendeinen technischen Grund gibt; es ist wahrscheinlich nur eine Designentscheidung, die sie getroffen haben. Es erzwingt sicherlich bessere Kodierungspraktiken.


Weil Sie interne Logik auf eine andere Funktion trennen und diese Funktion aufrufen sollten ...

  1. von beiden Ereignishandlern
  2. getrennt vom Code, wenn Sie benötigen

Dies ist eine elegantere Lösung und viel einfacher zu warten.





realbasic