73 votes

Pourquoi est-ce une mauvaise pratique d'appeler un gestionnaire d'événements à partir de code?

Supposons que vous ayez un élément de menu et un bouton qui remplissent la même tâche. Pourquoi est-il déconseillé de placer le code de la tâche dans l'événement d'action d'un contrôle, puis d'appeler cet événement à partir de l'autre contrôle? Delphi autorise cela comme vb6 mais realbasic ne le fait pas et dit que vous devriez placer le code dans une méthode qui est ensuite appelée à la fois par le menu et le bouton.

83voto

Rob Kennedy Points 107381

C'est une question de la façon dont le programme est organisé. Dans le scénario que vous avez décrit, l'élément de menu comportement sera définie en termes de bouton:

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;

Aucune de ces trois implémentations de travail, mais pourquoi l'élément de menu être dépendant sur le bouton? Quel est si spécial à propos de la touche qu'il faut définir l'élément de menu? Si une nouvelle conception de l'INTERFACE utilisateur ont fait disparaître les boutons, qu'arriverait-il au menu? Une meilleure façon est de factoriser le gestionnaire d'événements, les actions, donc il est indépendant des contrôles qu'il est attaché. Il ya quelques façons de le faire:

  1. L'un est de se débarrasser de l' MenuItem1Click méthode tout à fait et affecter l' Button1Click méthode de l' MenuItem1.OnClick propriété de l'évènement. C'est déroutant de disposer de méthodes nommées pour les boutons assignés aux éléments de menu' événements, de sorte que vous aurez envie de renommer le gestionnaire d'événements, mais c'est OK, parce que contrairement à VB, Delphi la méthode de noms de ne pas définir ce que les événements qu'ils manipulent. Vous pouvez assigner n'importe quelle méthode pour tout gestionnaire d'événements aussi longtemps que les signatures. Les deux composants' OnClick des événements sont de type TNotifyEvent, de sorte qu'ils peuvent partager une même mise en œuvre. Nom de méthodes pour ce qu'ils font, pas ce qu'ils appartiennent.

  2. Une autre façon est de déplacer le bouton du gestionnaire d'événements code dans une méthode distincte, et ensuite appeler cette méthode à partir de deux composants gestionnaires d'événements:

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

    De cette façon, le code qui fait vraiment des choses n'est pas liée directement à l'une des composantes, et qui vous donne la liberté de changer les contrôles de plus en plus facilement, comme par exemple en renommant, ou de les remplacer par les différents contrôles. Séparer le code du composant nous conduit à la troisième voie:

  3. L' TAction composant, introduit en Delphi 4, est spécialement conçu pour la situation que vous avez décrite, où il y a de multiples chemins de l'INTERFACE utilisateur pour la même commande. (D'autres langages et environnements de développement de fournir des concepts similaires; il n'est pas unique à Delphes.) Mettez votre événement le code de gestion en TActions' OnExecute gestionnaire d'événements, et puis l'assigner une action à l' Action de la propriété de fois le bouton et le menu.

    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;
    

    Voulez ajouter un autre élément de l'INTERFACE utilisateur qui agit comme un bouton? Pas de problème. Ajouter, définissez ses Action de la propriété, et vous avez terminé. Pas besoin d'écrire plus de code pour rendre le nouveau contrôle de regarder et agir comme l'ancienne. Vous avez déjà écrit que le code une seule fois.

    TAction va au-delà des gestionnaires d'événements. Il vous permet de vous assurer que vos contrôles d'INTERFACE utilisateur uniformes paramètres de propriété, y compris les légendes, des conseils, de la visibilité, enabledness, et les icônes. Lorsqu'une commande n'est pas valide à l'époque, de définir l'action de l' Enabled de la propriété en conséquence, et de tout lien de contrôle sera automatiquement désactivé. Pas besoin de s'inquiéter à propos d'une commande d'être désactivé par le biais de la barre d'outils, mais toujours activée via le menu, par exemple. Vous pouvez même utiliser l'action de l' OnUpdate événement, afin que l'action peut mettre à jour lui-même basé sur les conditions actuelles, au lieu que vous avez besoin de savoir chaque fois que quelque chose se passe qui peut vous obliger à définir l' Enabled droit de propriété à l'écart.

15voto

smok1 Points 2393

Parce que vous devez séparer la logique interne d'une autre fonction et appeler cette fonction ...

  1. des deux gestionnaires d'événements
  2. séparément du code si vous avez besoin de

C'est une solution plus élégante et beaucoup plus facile à maintenir.

10voto

smok1 Points 2393

C'est une extension de réponse, comme promis. En 2000, nous avons commencé à écrire une application à l'aide de Delphi. Ce fut l'un EXE et quelques DLL contenant la logique. C'était l'industrie du film, donc il y avait des clients DLL, réservation DLL, box-office de la DLL et la facturation de la DLL. Lorsque l'utilisateur veut faire de la facturation, il ouvre le formulaire approprié, le client sélectionné à partir d'une liste, puis OnSelectItem logique chargé de clients théâtres à la prochaine zone de liste déroulante, puis, après la sélection de cinéma à côté OnSelectItem rempli troisième zone de liste déroulante avec les informations sur les films, qui n'a pas encore été facturés. La dernière partie du processus était de pousser le bouton "Facture". Tout a été fait comme un événement procédures.

Puis quelqu'un a décidé que nous devrions avoir le clavier étendu de soutien. Nous avons ajouté l'appel de gestionnaires d'événements à partir d'un autre, même les gestionnaires d'.. Le flux de travail de gestionnaires d'événements commencé à se compliquer.

Après deux ans, quelqu'un a décidé de mettre en œuvre une autre caractéristique – de sorte que l'utilisateur de travailler avec les données du client dans un autre module (clients module) doit être présenté avec un bouton intitulé "Facture ce client". Ce bouton feu de la facture formulaire et de le présenter dans un tel état, comme si c'était de l'utilisateur qui ont été manuellement en sélectionnant toutes les données (l'utilisateur a été d'être en mesure de regarder, de faire quelques réglages, et appuyez sur la touche de magie "Facture"). Depuis les données du client est une DLL et de la facturation était une autre, il était EXE qui était de passage de messages. Donc l'idée évidente est que les données du client développeur aura routine unique avec un seul ID en paramètre, et que toute cette logique est à l'intérieur de module de facturation.
Imaginez ce qui s'est passé. Depuis TOUTE logique était à l'intérieur de gestionnaires d'événements, nous avons passé énormément de temps, en fait en essayant de ne pas mettre en œuvre une logique, mais en essayant d'imiter l'activité de l'utilisateur – comme le choix des éléments, la suspension de l'Application.MessageBox à l'intérieur des gestionnaires d'événement à l'aide de variables GLOBALES, et ainsi de on. Imaginez si nous avions même de la simple logique des procédures appelées à l'intérieur de gestionnaires d'événements, nous aurions été en mesure d'introduire DoShowMessageBoxInsideProc variable Booléenne à la procédure de signature. Une telle procédure pourrait avoir été appelé avec le paramètre true si elle est appelée à partir du gestionnaire d'événement, et avec de FAUX paramètres lorsqu'il est appelé à l'externe.

C'est ce que m'ont appris à ne pas mettre de la logique directement à l'intérieur de GUI de gestionnaires d'événements, avec une exception possible de petits projets.

8voto

Chris Ballance Points 17329

Séparation des préoccupations. Un événement privé pour une classe doit être encapsulé dans cette classe et non appelé à partir de classes externes. Cela facilite la modification de votre projet si vous avez des interfaces solides entre les objets et minimisez les occurrences de plusieurs points d'entrée.

8voto

Gerald Points 13865

Supposons qu'à un certain moment, vous décidez que l'élément de menu n'a plus de sens, et que vous voulez vous débarrasser de l'élément de menu. Si vous avez juste une autre contrôle pointant vers l'élément de menu du gestionnaire d'événements, qui ne pourrait pas être un gros problème, vous pouvez simplement copier le code dans le bouton du gestionnaire d'événement. Mais si vous avez plusieurs façons différentes, le code peut être invoquée, vous aurez à faire beaucoup de modification.

Personnellement, j'aime la façon Qt gère cela. Il y a une QAction classe avec son propre gestionnaire d'événement qui peut être accroché, et puis la QAction est associée à des éléments de l'INTERFACE utilisateur qui en ont besoin pour s'acquitter de cette tâche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X